源码如下:
public class Main {
public static void main(String[] args) {
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
HashSet<String> s = new HashSet<>();
String id = "x12323";
for(int j = 0; j<20; j++) {
fixedThreadPool.execute(() -> {
System.out.println("运行线程: " + Thread.currentThread().getName());
String lockObj = s.getClass().toString() + id;
synchronized (lockObj) {
if (s.isEmpty()) {
System.out.println(Thread.currentThread().getName() + " : lockObj(" + lockObj+ ") hash code ("
+ lockObj.hashCode() + "),集合 s 为空吗:" + s.isEmpty());
s.add("good");
}
}
});
}
}
}
运行结果:
运行线程: pool-1-thread-1
运行线程: pool-1-thread-2
pool-1-thread-1 : lockObj(class java.util.HashSetx12323) hash code (-1786802297),集合 s 为空吗 :true
pool-1-thread-2 : lockObj(class java.util.HashSetx12323) hash code (-1786802297),集合 s 为空吗:true
运行线程: pool-1-thread-1
运行线程: pool-1-thread-1
运行线程: pool-1-thread-1
运行线程: pool-1-thread-1
运行线程: pool-1-thread-1
运行线程: pool-1-thread-1
运行线程: pool-1-thread-1
运行线程: pool-1-thread-1
运行线程: pool-1-thread-1
运行线程: pool-1-thread-1
运行线程: pool-1-thread-1
运行线程: pool-1-thread-1
运行线程: pool-1-thread-1
运行线程: pool-1-thread-1
运行线程: pool-1-thread-1
运行线程: pool-1-thread-5
运行线程: pool-1-thread-4
运行线程: pool-1-thread-3
为何这两个线程都进入互斥块了?
pool-1-thread-1 : lockObj(class java.util.HashSetx12323) hash code (-1786802297),集合 s 为空吗 :true
pool-1-thread-2 : lockObj(class java.util.HashSetx12323) hash code (-1786802297),集合 s 为空吗:true
更新 编译后常量池和字节码
默认编译
Java 8
查看编译后的class
文件constant_pool_count
为 146,其中只有
再看字节码部分:
037: aload_0
是从局部变量表里加载0号值041: ldc #5->x12323
是加载常量池6号值所以说
classname
不是常量值入栈,个人猜测是因为有泛型之后,
Java
又选择了泛型擦除,很多东西要等到运行时才能确定,getClass
这个操作应该就是带有不确定性,所以它的结果不进入常量池。更新 显示更多的调试信息
右击变量区,点击
Customize Data Views...
选你要的,我是全部勾选了。
原答案
hasCode
一样并不说明两个String
实例是同一个,String.hashCode
只是计算value
值,同样内容的情况下hashCode
会一样的。在
String lockObj = s.getClass().toString() + id;
后边加一句打印语句并下断点。第一次循环时创建的
lockObj
的内存数据是这样的这是第二次的内存数据。
可以看到,这里的两个
String
的值即使一样,它们的value
属性却不是同一个指向。所以你锁的对象其实本就不是同一个,把
lockObj
的声明放到循环外部后你锁的才是同一个对象。个人猜测:
你的
lockObj
是一个依赖运行时变量hashCode
而拼接的字符串,而且不是常量字符串的拼接,也就是说编译器无法在编译期优化掉它,也就不会在字符串池里放这个。也就是说,每次循环时
JVM
会重新创建一个byte[]
,这就导致了每次lockObj
实际上不是同一个实例。