文章目录
锁的分类
Java中锁分为以下几种:
-
乐观锁、悲观锁
-
共享锁、独享锁
-
公平锁、非公平锁
-
互斥锁、读写锁
-
可重入、 不可重入锁
-
synchronized 锁升级(无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁) (since JDK1.6)
这些锁的分类并不全是指锁的状态,有的指锁的特性,有的指锁的设计,下面总结的内容是对每个锁的名词进行一定的解释。
1. 乐观锁 VS 悲观锁
两种锁只是一种概念
乐观锁:
乐观锁认为一个线程去拿数据的时候不会有其他线程对数据进行更改,所以不会上锁。实现方式:CAS机制、版本号机制
悲观锁:
悲观锁认为一个线程去拿数据时一定会有其他线程对数据进行更改。所以一个线程在拿数据的时候都会顺便加锁,这样别的线程此时想拿这个数据就会阻塞。比如Java里面的synchronized关键字的实现就是悲观锁。实现方式:就是加锁。
根据从上面的概念描述我们可以发现:
- 悲观锁适合写操作多的场景,先加锁可以保证写操作时数据正确。
- 乐观锁适合读操作多的场景,不加锁的特点能够使其读操作的性能大幅提升
通过调用方式示例,我们可以发现悲观锁基本都是在显式的锁定之后再操作同步资源,而乐观锁则直接去操作同步资源。那么,为何乐观锁能够做到不锁定同步资源也可以正确的实现线程同步呢?具体可以参看JUC原子类: CAS, Unsafe和原子类详解。
2. 独享锁 VS 共享锁
-
独享锁和共享锁同样是一种概念。 我们先介绍一下具体的概念,然后通过ReentrantLock和ReentrantReadWriteLock的源码来介绍独享锁和共享锁。
-
独享锁也叫排他锁,是指该锁一次只能被一个线程所持有。如果线程T对数据A加上排它锁后,则其他线程不能再对A加任何类型的锁。获得排它锁的线程即能读数据又能修改数据。JDK中的synchronized和JUC中Lock的实现类就是互斥锁。
-
共享锁是指该锁可被多个线程所持有。如果线程T对数据A加上共享锁后,则其他线程只能对A再加共享锁,不能加排它锁。获得共享锁的线程只能读数据,不能修改数据。
-
独享锁与共享锁也是通过AQS来实现的,通过实现不同的方法,来实现独享或者共享。
下图为ReentrantReadWriteLock的部分源码: