为何要这么说呢, 由于笔者被这个坑过(实际上是本身坑本身)╮(╯_╰)╭java
先看一段synchronized 的详解:并发
synchronized 是 java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,可以保证在同一时刻最多只有一个线程执行该段代码。异步
1、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程获得执行。另外一个线程必须等待当前线程执行完这个代码块之后才能执行该代码块。ide
2、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另外一个线程仍然能够访问该object中的非synchronized(this)同步代码块。this
3、尤为关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其余线程对object中全部其它synchronized(this)同步代码块的访问将被阻塞。spa
4、第三个例子一样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就得到了这个object的对象锁。结果,其它线程对该object对象全部同步代码部分的访问都被暂时阻塞。线程
5、以上规则对其它对象锁一样适用.code
简单来讲, synchronized就是为当前的线程声明一个锁, 拥有这个锁的线程能够执行区块里面的指令, 其余的线程只能等待获取锁, 而后才能相同的操做.对象
这个很好用, 可是笔者遇到另外一种比较奇葩的状况.ci
1. 在同一类中, 有两个方法是用了synchronized关键字声明
2. 在执行完其中一个方法的时候, 须要等待另外一个方法(异步线程回调)也执行完, 因此用了一个countDownLatch来作等待
3. 代码解构以下:
synchronized void a(){ countDownLatch = new CountDownLatch(1); // do someing countDownLatch.await(); } synchronized void b(){ countDownLatch.countDown(); }
其中
a方法由主线程执行, b方法由异步线程执行后回调
执行结果是:
主线程执行 a方法后开始卡住, 再也不往下作, 任你等多久都没用.
这是一个很经典的死锁问题
a等待b执行, 其实不要看b是回调的, b也在等待a执行. 为何呢? synchronized 起了做用.
通常来讲, 咱们要synchronized一段代码块的时候, 咱们须要使用一个共享变量来锁住, 好比:
byte[] mutex = new byte[0]; void a1(){ synchronized(mutex){ //dosomething } } void b1(){ synchronized(mutex){ // dosomething } }
若是把a方法和b方法的内容分别迁移到 a1和b1 方法的synchronized块里面, 就很好理解了.
a1执行完后会间接等待(countDownLatch)b1方法执行
然而因为 a1 中的mutex并无释放, 就开始等待b1了, 这时候, 即便是异步的回调b1方法, 因为须要等待mutex释放锁, 因此b方法并不会执行
因而就引发了死锁
而这里的synchronized关键字放在方法前面, 起的做用就是同样的. 只是java语言帮你隐去了mutex的声明和使用而已. 同一个对象中的synchronized 方法用到的mutex是相同的, 因此即便是异步回调, 也会引发死锁, 因此要注意这个问题. 这种级别的错误是属于synchronized关键字使用不当. 不要乱用, 并且要用对.
那么这样的 隐形的mutex 对象到底是 什么呢?
很容易想到的就是 实例自己. 由于这样就不用去定义新的对象了作锁了. 为了证实这个设想, 能够写一段程序来证实.
思路很简单, 定义一个类, 有两个方法, 一个方法声明为 synchronized, 一个在 方法体里面使用synchronized(this), 而后启动两个线程, 来分别调用这两个方法, 若是两个方法之间发生锁竞争(等待)的话, 就能够说明 方法声明的 synchronized 中的隐形的mutex其实就是 实例自己了.
public class MultiThreadSync { public synchronized void m1() throws InterruptedException{ System. out.println("m1 call" ); Thread. sleep(2000); System. out.println("m1 call done" ); } public void m2() throws InterruptedException{ synchronized (this ) { System. out.println("m2 call" ); Thread. sleep(2000); System. out.println("m2 call done" ); } } public static void main(String[] args) { final MultiThreadSync thisObj = new MultiThreadSync(); Thread t1 = new Thread(){ @Override public void run() { try { thisObj.m1(); } catch (InterruptedException e) { e.printStackTrace(); } } }; Thread t2 = new Thread(){ @Override public void run() { try { thisObj.m2(); } catch (InterruptedException e) { e.printStackTrace(); } } }; t1.start(); t2.start(); } }
结果输出是:
m1 call
m1 call done
m2 call
m2 call done
说明方法m2的sync块等待了m1的执行. 这样就能够证明 上面的设想了.
另外须要说明的是, 当sync加在 static的方法上的时候, 因为是类级别的方法, 因此锁住的对象是当前类的class实例. 一样也能够写程序进行证实.这里略.
因此方法的synchronized 关键字, 在阅读的时候能够自动替换为synchronized(this){}就很好理解了.
void method(){ void synchronized method(){ synchronized(this){ // biz code // biz code } ------>>> } }