package cn.enjoyedu.ch1.syn;
/**
* 类说明:错误的加锁和原因分析
*/
public class TestIntegerSyn {
public static void main(String[] args) throws InterruptedException {
Worker worker=new Worker(1);
for(int i=0;i<5;i++) {
//注意:这里传入的是同1个worker,因此Worker类里面的Integer i 和 Object obj,5个线程是共享的
//不要误认为是5个worker,而是5个线程用的是同1个worker对象
new Thread(worker).start();
}
}
private static class Worker implements Runnable{
private Integer i;//5个线程共享
private Object obj = new Object();//5个线程共享
public Worker(Integer i) {
this.i=i;
}
@Override
public void run() {
// 如果锁的是i:
// >>>假设线程-0先拿到锁,进入同步代码块。其他4个线程被block在对象i=Integer(1)的锁上。由于i++装箱操作会return一个新的integer对象赋给i,此时被锁的i变为i=Integer(2)
// 原本4个线程被block在对象i=Integer(1)的锁上(因为线程-0已经拿到锁),但是此时锁的对象变成了i=Integer(2),因此这4个线程可以重新竞争进入代码块。
// >>>当线程-0 i++后,不等它执行完同步代码块释放掉锁,线程-1就会直接进来操作。此时线程-1 拿的锁是线程-0加一之后的对象。。以此类推,等线程-1加一后,线程-2拿的锁是线程-1加一之后的对象
// >>>又因为5个线程共享i,因此,线程-0 /其他线程 sleep之前和之后输出的i会发生变化。因为在sleep的时候,别的线程进来操作了。
//
// 如果锁的是obj:
// >>>obj同样是5个线程共享的,但由于没有线程对其操作。因此5个线程始终在竞争同一把锁,可以互斥运行。
synchronized (i) { //obj
Thread thread=Thread.currentThread();
System.out.println(thread.getName()+"-------@"+System.identityHashCode(i));
i++;
System.out.println(thread.getName()+"-------"+i+"-@"+System.identityHashCode(i));
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(thread.getName()+"-------"+i+"--@"+System.identityHashCode(i));
}
}
}
}
如果锁的是可变对象,i 。结果:5个线程会无法互斥。
Thread-0-------@129460851 Thread-0-------2-@158369013 Thread-2-------@158369013 Thread-2-------3-@1898749074 Thread-3-------@1898749074 Thread-3-------4-@1763156594 Thread-4-------@1763156594 Thread-4-------5-@825455695 Thread-4-------5--@825455695 Thread-2-------5--@825455695 Thread-0-------5--@825455695 Thread-3-------5--@825455695 Thread-1-------@825455695 Thread-1-------6-@1779415244 Thread-1-------6--@1779415244
修改成不可变对象, obj。结果:5个线程必须互斥执行,一个执行完了再执行下一个。
Thread-0-------@1898749074 Thread-0-------2-@2146135239 Thread-0-------2--@2146135239 Thread-4-------@2146135239 Thread-4-------3-@299052996 Thread-4-------3--@299052996 Thread-3-------@299052996 Thread-3-------4-@976006504 Thread-3-------4--@976006504 Thread-2-------@976006504 Thread-2-------5-@658235253 Thread-2-------5--@658235253 Thread-1-------@658235253 Thread-1-------6-@825455695 Thread-1-------6--@825455695
原因:虽然我们对 i 进行了加锁,但是
但是当我们反编译这个类的 class 文件后,可以看到 i++实际是,
本质上是返回了一个新的 Integer 对象。也就是每个线程实际加锁的是不同 的 Integer 对象。
原文:https://www.cnblogs.com/frankcui/p/12490093.html