1、java锁分类
这些锁的分类并不全是指锁的状态,有的指锁的特性,有的指锁的设计。(就是从不同的维度去区分锁)
概念上分类:
- 乐观锁、悲观锁
- 独享锁、共享锁
- 可重入锁
- 公平锁、非公平锁
- 分段锁
- 锁升级(无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁) JDK1.6
具体实现分类(具体的锁):
互斥锁、读写锁 (C++中有)
锁设计(不是一种具体锁):
分段锁
详情:
-
乐观锁:乐观锁认为一个线程去拿数据的时候不会有其他线程对数据进行更改,所以不会上锁。
实现方式:CAS机制、版本号机制
-
悲观锁:认为一个线程去拿数据时一定会有其他线程对数据进行更改。所以一个线程在拿数据的时候都会顺便加锁,这样别的线程此时想拿这个数据就会阻塞。比如Java里面的synchronized关键字的实现就是悲观锁。实现方式:就是加锁。
-
独享锁:该锁一次只能被一个线程所持有。(synchronized是独享锁、可重入锁ReentrantLock是独享锁)
-
共享锁:该锁可以被多个线程所持有。(读写锁ReentrantReadWriteLock中的读锁ReadLock是共享锁,写锁WriteLock是独享锁。(Reentrant:可重入的))
-
互斥锁: 的具体实现就是synchronized、ReentrantLock。(ReentrantLock是JDK1.5的新特性,采用ReentrantLock可以完全替代替换synchronized传统的锁机制,更加灵活。)
-
读写锁:具体实现就是读写锁ReadWriteLock。 //TODO
-
可重入锁:对于同一个线程在外层方法获取锁的时候,在进入内层方法时也会自动获取锁。(优点:避免死锁。举例:ReentrantLock、synchronized)
-
公平锁:多个线程相互竞争时要排队,多个线程按照申请锁的顺序来获取锁。
-
非公平锁:多个线程相互竞争时,先尝试插队,插队失败再排队,比如:synchronized、ReentrantLock
-
分段锁:并不是具体的一种锁,只是一种锁的设计。
-
偏向锁 & 轻量级锁 & 重量级锁:为了减少获得锁和释放锁所带来的性能消耗。(锁不仅不存在多线程竞争,而且总是由同一线程多次获得,为了不让这个线程每次获得锁都需要CAS操作的性能消耗,就引入了偏向锁。当一个线程访问对象并获取锁时,会在对象头里存储锁偏向的这个线程的ID,以后该线程再访问该对象时只需判断对象头的Mark Word里是否有这个线程的ID,如果有就不需要进行CAS操作,这就是偏向锁。当线程竞争更激烈时,偏向锁就会升级为轻量级锁,轻量级锁认为虽然竞争是存在的,但是理想情况下竞争的程度很低,通过自旋方式等待一会儿上一个线程就会释放锁,但是当自旋超过了一定次数,或者一个线程持有锁,一个线程在自旋,又来了第三个线程访问时(反正就是竞争继续加大了),轻量级锁就会膨胀为重量级锁,重量级锁就是Synchronized,重量级锁会使除了此时拥有锁的线程以外的线程都阻塞。)
常用锁:
-
Synchronized:它就是一个:非公平,悲观,独享,互斥,可重入的重量级锁
-
ReentrantLock:它是一个:默认非公平但可实现公平的,悲观,独享,互斥,可重入,重量级锁。
-
ReentrantReadWriteLocK:它是一个,默认非公平但可实现公平的,悲观,写独享,读共享,读写,可重入,重量级锁。
备注说明(最重要):
(1)在java内部,同一线程在调用自己类中其他synchronized方法/块或调用父类的synchronized方法/块都不会阻碍该线程的执行,就是说同一线程对同一个对象锁是可重入的,而且同一个线程可以获取同一把锁多次,也就是可以多次重入。 因为java线程是基于“每线程(per-thread)”,而不是基于“每调用(per-invocation)”的(java中线程获得对象锁的操作是以每线程为粒度的,per-invocation互斥体获得对象锁的操作是以每调用作为粒度的)
我们再来看看重入锁是怎么实现可重入性的,其实现方法是为每个锁关联一个线程持有者和计数器,当计数器为0时表示该锁没有被任何线程持有,那么任何线程都可能获得该锁而调用相应的方法;当某一线程请求成功后,JVM会记下锁的持有线程,并且将计数器置为1;此时其它线程请求该锁,则必须等待;而该持有锁的线程如果再次请求这个锁,就可以再次拿到这个锁,同时计数器会递增;当线程退出同步代码块时,计数器会递减,如果计数器为0,则释放该锁。synchronized是隐式锁,位于concurrent.locks保重的Reentranrlock是显示锁,注意需要手动的unlock解锁
(2)一个线程获取多少次锁,就必须释放多少次锁。这对于内置锁也是适用的,每一次进入和离开synchornized方法(代码块),就是一次完整的锁获取和释放。
(3)锁降级:从写锁变成读锁;锁升级:从读锁变成写锁