为什么同步锁不适用于字符串连接的原因
Why is reason that synchronized lock not working on String concatenation
我正在使用以下代码。我无法实现同步。按照我的说法,字符串池概念应该在这里起作用。
我想知道问题背后的原因,而不是替代方案。
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class SynchronizationBug {
private static Map<String,Date> concurrentMap=new ConcurrentHashMap();
public static void start(String processId) throws InterruptedException{
final String lock="log"+processId;
synchronized (lock) {
if(concurrentMap.containsKey(processId)) {
System.out.println("Process is already working and started at "+concurrentMap.get(processId));
return;
}
concurrentMap.put(processId,new Date());
}
}
public static void main(String[] args) throws InterruptedException {
// System.out.println(isPrime(10));
final String pId="p1";
Thread t1=new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
try {
start(pId);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
Thread t2=new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
try {
start(pId);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
t1.start();
t2.start();
t1.join();
t2.join();
for (String s:concurrentMap.keySet()) {
System.out.println(s+" - "+concurrentMap.get(s));
}
}
}
如果我锁定作为方法参数传递的 processId,那么一切都会按预期进行。
Java 这种行为的原因是什么?我已经在 Java8 Eclipse IDE.
上测试过了
编辑:
如果字符串池概念没有相关性,那么下面的代码如何工作。
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class SynchronizationBug {
private static Map<String,Date> concurrentMap=new ConcurrentHashMap();
public static void start(String processId) throws InterruptedException{
final String lock="log"+processId;
synchronized (processId) {
if(concurrentMap.containsKey(processId)) {
System.out.println("Process is already working and started at "+concurrentMap.get(processId));
return;
}
concurrentMap.put(processId,new Date());
}
}
public static void main(String[] args) throws InterruptedException {
// System.out.println(isPrime(10));
final String pId1="p1";
final String pId2="p1";
Thread t1=new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
try {
start(pId1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
Thread t2=new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
try {
start(pId2);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
t1.start();
t2.start();
t1.join();
t2.join();
for (String s:concurrentMap.keySet()) {
System.out.println(s+" - "+concurrentMap.get(s));
}
}
}
在这里,我将两个具有相同 value.Due 的不同字符串对象传递给它们在池中的相同引用,锁定工作正常。
请指正....
此行为的原因是,如果操作数不是两个编译时常量表达式,则字符串连接会导致创建字符串的新实例。见 here in the language spec:
The String object is newly created (§12.5) unless the expression is a constant expression (§15.29).
方法参数不是编译时常量表达式,因为可以使用参数的任何值调用该方法。
而且,因为当前线程是唯一可以访问这个新创建的字符串的线程,所以对其进行同步没有任何效果。
既然你说你不想知道其他选择,那我就到此为止吧。
尽管我指出了规范的相关部分,但您似乎不相信您要同步的字符串是不同的。尝试在 synchronized
:
之前添加这一行
System.out.println(System.identityHashCode(lock)); // Add this
synchronized (lock) {
// ...
这将打印出锁的“身份哈希码”,它与 lock.hashCode()
不同,后者基于字符串的值。这将显示 synchronized
正在同步不同的值(除非你是 异常 lucky/unlucky,因为哈希冲突不太可能但可能发生)。
我正在使用以下代码。我无法实现同步。按照我的说法,字符串池概念应该在这里起作用。
我想知道问题背后的原因,而不是替代方案。
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class SynchronizationBug {
private static Map<String,Date> concurrentMap=new ConcurrentHashMap();
public static void start(String processId) throws InterruptedException{
final String lock="log"+processId;
synchronized (lock) {
if(concurrentMap.containsKey(processId)) {
System.out.println("Process is already working and started at "+concurrentMap.get(processId));
return;
}
concurrentMap.put(processId,new Date());
}
}
public static void main(String[] args) throws InterruptedException {
// System.out.println(isPrime(10));
final String pId="p1";
Thread t1=new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
try {
start(pId);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
Thread t2=new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
try {
start(pId);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
t1.start();
t2.start();
t1.join();
t2.join();
for (String s:concurrentMap.keySet()) {
System.out.println(s+" - "+concurrentMap.get(s));
}
}
}
如果我锁定作为方法参数传递的 processId,那么一切都会按预期进行。 Java 这种行为的原因是什么?我已经在 Java8 Eclipse IDE.
上测试过了编辑: 如果字符串池概念没有相关性,那么下面的代码如何工作。
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class SynchronizationBug {
private static Map<String,Date> concurrentMap=new ConcurrentHashMap();
public static void start(String processId) throws InterruptedException{
final String lock="log"+processId;
synchronized (processId) {
if(concurrentMap.containsKey(processId)) {
System.out.println("Process is already working and started at "+concurrentMap.get(processId));
return;
}
concurrentMap.put(processId,new Date());
}
}
public static void main(String[] args) throws InterruptedException {
// System.out.println(isPrime(10));
final String pId1="p1";
final String pId2="p1";
Thread t1=new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
try {
start(pId1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
Thread t2=new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
try {
start(pId2);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
t1.start();
t2.start();
t1.join();
t2.join();
for (String s:concurrentMap.keySet()) {
System.out.println(s+" - "+concurrentMap.get(s));
}
}
}
在这里,我将两个具有相同 value.Due 的不同字符串对象传递给它们在池中的相同引用,锁定工作正常。 请指正....
此行为的原因是,如果操作数不是两个编译时常量表达式,则字符串连接会导致创建字符串的新实例。见 here in the language spec:
The String object is newly created (§12.5) unless the expression is a constant expression (§15.29).
方法参数不是编译时常量表达式,因为可以使用参数的任何值调用该方法。
而且,因为当前线程是唯一可以访问这个新创建的字符串的线程,所以对其进行同步没有任何效果。
既然你说你不想知道其他选择,那我就到此为止吧。
尽管我指出了规范的相关部分,但您似乎不相信您要同步的字符串是不同的。尝试在 synchronized
:
System.out.println(System.identityHashCode(lock)); // Add this
synchronized (lock) {
// ...
这将打印出锁的“身份哈希码”,它与 lock.hashCode()
不同,后者基于字符串的值。这将显示 synchronized
正在同步不同的值(除非你是 异常 lucky/unlucky,因为哈希冲突不太可能但可能发生)。