互斥锁
锁的可重入性
因为在Concurrent包中的锁都是可重入锁,所以一般都命名为ReentrantX,因为所有的锁,可重入锁是指当一个线程调用object.lock()拿到锁,进入互斥区后,再次调用object.lock(),仍然可以拿到该锁。很显然,通常的锁都要设计成可重入的,否则就会发生死锁。
synchronized关键字同样也是可重入锁。
Lock是一个接口,其定义如下:
常用的方法是lock()/unlock()。lock()不能被中断,对应的lockInterruptibly()可以被中断。ReentrantLock本身并没有代码逻辑,实现都在其内部类Sync中。
锁的公平性与非公平性
Sync是一个抽象类,它有两个子类FairSync与NonfairSync,分别对应公平锁和非公平锁。默认是非公平的。
一个线程来了之后,看到有很多线程在排队,自己排到队伍末尾,这叫公平;线程来了之后直接去抢锁,这叫做不公平。这里默认设置的是非公平锁,其实是为了提高效率,减少线程的切换。
锁实现的基本原理
Sync的父类是AbstractQueuedSynchronizer经常被称作队列同步器(AQS),这个类非常关键。该类的父类是AbstractOwnableSynchronizer。
为了实现一把具有阻塞和唤醒功能的锁,需要几个核心要素:
(1)、需要一个state变量,标记锁的状态,state变量至少有两个值:0,1。对state变量的操作,要确保线程安全,也就是会用到CAS。
(2)、需要记录当前是哪个线程持有锁。
(3)、需要底层支持对一个线程进行阻塞或唤醒操作。
(4)、需要一个队列维护所有阻塞的线程。这个队列也必须是线程安全的无锁队列,也需要CAS。
Unsafe中,提供了阻塞或唤醒线程的一对操作原语,也就是park\unpark
public native void unpark(Object var1);
public native void park(boolean, long var2);
读写锁
ReentrantReadWriteLock
读写锁实现的基本原理
可以理解为一把锁,线程分为两类:读线程和写线程。读线程和读线程之间不互斥,读线程和写线程互斥,写线程和写线程也互斥。