本文出自 云图畅想-还原数据的价值 ,转载时请注明出处及相应链接。
本文永久链接: http://www.maoxiangyi.cn/index.php/archives/216
Java专题(1)-Java概述、Java发展、Java虚拟机
Java专题(6)-Java的synchronized和Lock
synchronized
把代码块声明为 synchronized,有两个重要后果,通常是指该代码具有 原子性(atomicity)和 可见性(visibility)。
原子性意味着一个线程一次只能执行由一个指定监控对象(lock)保护的代码,从而防止多个线程在更新共享状态时相互冲突。
当synchronized作用在方法上时,锁住的便是对象实例(this);
当synchronized作用在静态方法时锁住的便是对象对应的Class实例,因为Class数据存在于永久带,因此静态方法锁相当于该类的一个全局锁;
当synchronized作用于某一个对象实例时,锁住的便是对应的代码块
我们知道锁存在Java对象头里。如果对象是数组类型,则虚拟机用3个Word(字宽)存储对象头,如果对象是非数组类型,则用2字宽存储对象头。而在HotSpot JVM实现中,锁有个专门的名字:对象监视器。
JAVA中的监视器实际是一个代码块,这段代码块同一时刻只允许被一个线程执行。线程要想执行这段代码块的唯一方式是获得监视器。
监视器有两种同步方式:互斥与协作。多线程环境下线程之间如果需要共享数据,需要解决互斥访问数据的问题,监视器可以确保监视器上的数据在同一时刻只会有一个线程在访问。什么时候需要协作?比如:一个线程向缓冲区写数据,另一个线程从缓冲区读数据,如果读线程发现缓冲区为空就会等待,当写线程向缓冲区写入数据,就会唤醒读线程,这里读线程和写线程就是一个合作关系。JVM通过Object类的wait方法来使自己等待,在调用wait方法后,该线程会释放它持有的监视器,直到其他线程通知它才有执行的机会。一个线程调用notify方法通知在等待的线程,这个等待的线程并不会马上执行,而是要通知线程释放监视器后,它重新获取监视器才有执行的机会。如果刚好唤醒的这个线程需要的监视器被其他线程抢占,那么这个线程会继续等待。object类中的notifyAll方法可以解决这个问题,它可以唤醒所有等待的线程,总有一个线程执行。
ReentrantLock
在java.util.concurrent.locks包中有很多Lock的实现类,常用的有ReentrantLock、ReadWriteLock(实现类ReentrantReadWriteLock)。
经过观察ReentrantLock把所有Lock接口的操作都委派到一个Sync类上,sync有两个子类,分别为了支持公平锁和非公平锁而定义,默认情况下为非公平锁。
ReentrantLock提供了多样化的同步,比如有时间限制的同步,可以被Interrupt的同步(synchronized的同步是不能Interrupt的)等。在资源竞争不激烈的情形下,性能稍微比synchronized差点点。但是当同步非常激烈的时候,synchronized的性能一下子能下降好几十倍。而ReentrantLock确还能维持常态。
总结:
1:Lock 的锁定是通过Java代码实现的,而 synchronized 是在 JVM 层面上实现的。
2:ReentrantLock可以成为公平锁。所谓公平锁就是让等待最长的线程最早获得该锁(获得锁的顺序和申请锁的顺序是一致的);与之对应的synchronized是非公平的、当然ReentrantLock也可以成为非公平锁;只是公平锁的性能相对差一些。
3:synchronized锁是基于对象的,一个线程占有了锁,其他线程不能再进入这个对象的任何synchronized方法。而ReentrantLock基于对象中段的,可以将对象划分成不同的块,使用不同的锁子。
4:synchronized 在锁定时如果方法块抛出异常,JVM 会自动将锁释放掉,不会因为出了异常没有释放锁造成线程死锁。但是 Lock 的话就享受不到 JVM 带来自动的功能,出现异常时必须在 finally 将锁释放掉,否则将会引起死锁。
5:在资源竞争不是很激烈的情况下,偶尔会有同步的情形下,synchronized是很合适的。原因在于,编译程序通常会尽可能的进行优化synchronize,另外可读性非常好,不管用没用过5.0多线程包的程序员都能理解。
6:ReentrantLock提供了多样化的同步,比如有时间限制的同步,可以被Interrupt的同步(synchronized的同步是不能Interrupt的)等。在资源竞争不激烈的情形下,性能稍微比synchronized差点点。但是当同步非常激烈的时候,synchronized的性能一下子能下降好几十倍。而ReentrantLock确还能维持常态。
参考资料: