Java 整数对象用作同步块中的锁时可能出现并发问题
在处理多线程程序时,我观察到了非常奇怪的行为 当Integer对象用作锁时,似乎可以在同步块中使用多个线程。 甚至认为这是不可能的 如果我在下面的程序中使用任何其他静态成员,如's'、'o'和'c'define,它将按预期工作 代码- 正如您在打印“101112”时看到的,有两个线程在同步块中执行 是我做错了什么还是我错过了什么 这与幕后的一些优化有关吗? 因为如果我用“ii”来锁定,一切都会完美工作。 使用“i”时,也会打印“fail”,但很少打印。 下面是用于运行该程序的java版本。java版本java版本“1.7.0_51”java(TM)SE运行时环境(构建 1.7.0_51-b13)Java热点(TM)客户端虚拟机(构建24.51-b03,混合模式,共享) 您可以使用下面的程序运行程序来运行这一系列的时间,并查看结果Java 整数对象用作同步块中的锁时可能出现并发问题,java,multithreading,Java,Multithreading,在处理多线程程序时,我观察到了非常奇怪的行为 当Integer对象用作锁时,似乎可以在同步块中使用多个线程。 甚至认为这是不可能的 如果我在下面的程序中使用任何其他静态成员,如's'、'o'和'c'define,它将按预期工作 代码- 正如您在打印“101112”时看到的,有两个线程在同步块中执行 是我做错了什么还是我错过了什么 这与幕后的一些优化有关吗? 因为如果我用“ii”来锁定,一切都会完美工作。 使用“i”时,也会打印“fail”,但很少打印。 下面是用于运行该程序的java版本。jav
public class MyThread extends Thread{
private static Integer ii=1;
private static Integer i=1;
private static String s="1";
private static Object o= new Object();
private static Class<MyThread> c= MyThread.class;
public void run(){
synchronized(ii){
System.out.print(i++ +" ");
System.out.print(i+" ");
}
}
public static void main(String[] str) throws InterruptedException{
for(int j=0;j<100;j++){
for(int i=0;i<100;i++){
MyThread t= new MyThread();
t.start();
}
Thread.sleep(50);
System.out.println();
MyThread t= new MyThread();
t.start();t.join();
if(i!=102)
System.out.println("fail");
Thread.sleep(50);
i=1;
System.out.println();
}
}
}
公共类MyThread扩展线程{
私有静态整数ii=1;
私有静态整数i=1;
私有静态字符串s=“1”;
私有静态对象o=新对象();
私有静态类c=MyThread.Class;
公开募捐{
同步(二){
系统输出打印(i+++);
系统输出打印(i+“”);
}
}
公共静态void main(字符串[]str)引发InterruptedException{
对于(intj=0;j
相当于
i = Integer.valueOf(i.intValue() + 1)
换句话说,i
现在引用的对象不同于您最初同步的对象
如果线程在i
发生更改后碰巧到达synchronized
块,它也会进入,因为它在不同的对象上获取监视器
因为如果我用“ii”来锁定,一切都会很完美
您没有在任何地方更改对ii
的引用。我认为问题在于您通过在同步块内执行I++
来更改“I”的值,这导致分配给I
的对象每次都不同(在Eclipse中进行检查,查看增量操作前后变量的对象id).
是一个不可变的类。当您对它执行类似于++
的操作时,您正在创建一个新实例。这可能会导致线程在不同的对象上同步,使它们能够同时访问已同步的块。这正是锁应该声明为final的原因代码>。这是一个例子,说明了你可以如何射中自己的脚。在Integer上同步似乎是个坏主意:这是我不喜欢自动装箱的原因之一。尽管在Integer上同步是一件危险的事,因为类Integer
所做的实例缓存可能会产生意外的影响。我也是这样做的t意思是如果我们锁定String
对象,并对synchronized
块中的String
对象进行一些处理,使其指向其他字符串对象,我们将获得相同的行为?@sakurasynchronized
通过获取变量(或表达式)引用的对象上的监视器来工作。如果您更改该变量作为引用的内容,并且不同的线程到达该变量上的同步块,则它会从不同的对象获取锁。从这个意义上说,是的。
public class MyThread extends Thread{
private static Integer ii=1;
private static Integer i=1;
private static String s="1";
private static Object o= new Object();
private static Class<MyThread> c= MyThread.class;
public void run(){
synchronized(ii){
System.out.print(i++ +" ");
System.out.print(i+" ");
}
}
public static void main(String[] str) throws InterruptedException{
for(int j=0;j<100;j++){
for(int i=0;i<100;i++){
MyThread t= new MyThread();
t.start();
}
Thread.sleep(50);
System.out.println();
MyThread t= new MyThread();
t.start();t.join();
if(i!=102)
System.out.println("fail");
Thread.sleep(50);
i=1;
System.out.println();
}
}
}
i++
i = Integer.valueOf(i.intValue() + 1)