Contents

Java 多线程 - 深入理解synchronized关键字

Contents

synchronized关键字提供了一种互斥机制,也就是说在同一时刻,只能有一个线程访问同步资源,很多资料、书籍将synchronized(mutex)称为锁,其实这种说法是不严谨的,准确地讲应该是某线程获取了与mutex关联的monitor锁(当然写程序的时候知道它想要表达的语义即可)。

使用JDK命令javap对Mutex class进行反汇编,输出了大量的JVM指令,在这些指令中,你将发现monitor enter和monitor exit是成对出现的(有些时候会出现一个monitor enter多个monitor exit,但是每一个monitor exit之前必有对应的monitor enter,这是肯定的).

  • Monitorenter 每个对象都与一个monitor相关联,一个monitor的lock的锁只能被一个线程在同一时间获得,在一个线程尝试获得与对象关联monitor的所有权时会发生如下的几件事情。
    • 如果monitor的计数器为0,则意味着该monitor的lock还没有被获得,某个线程获得之后将立即对该计数器加一,从此该线程就是这个monitor的所有者了。
    • 如果一个已经拥有该monitor所有权的线程重入,则会导致monitor计数器再次累加。
    • 如果monitor已经被其他线程所拥有,则其他线程尝试获取该monitor的所有权时,会被陷入阻塞状态直到monitor计数器变为0,才能再次尝试获取对monitor的所有权。
  • Monitorexit 释放对monitor的所有权,想要释放对某个对象关联的monitor的所有权的前提是,你曾经获得了所有权。释放monitor所有权的过程比较简单,就是将monitor的计数器减一,如果计数器的结果为0,那就意味着该线程不再拥有对该monitor的所有权,通俗地讲就是解锁。与此同时被该monitor block的线程将再次尝试获得对该monitor的所有权。

使用synchronized方法需要注意几个问题:

  1. 与monitor关联的对象不能为空
  2. synchronized作用域太大 由于synchronized关键字存在排他性,也就是说所有的线程必须串行地经过synchronized保护的共享区域,如果synchronized作用域越大,则代表着其效率越低,甚至还会丧失并发的优势。
  3. 不同的monitor企图锁相同的方法
  4. 多个锁的交叉导致死锁