互斥锁ReentranLock
可重入锁:
线程获取到对象锁后,再次进入,不需要重新获取锁,synchronize就是可重入锁。
ReentrantLock也是可重入锁,原理:
内部逻辑都在Sync中,Sync是一个抽象类,有公平和非公平两种实现,Sync父类是AQS,维护了一个state变量,获取state,通过CAS获取锁。并且记录哪个线程获取锁,还有一个队列维护阻塞线程。
非公平锁就是判断state是否为0,为0就可以获取锁,非0就不能获取锁,同时判断非0的情况下,持有线程是不是自己,是自己就获取锁。不是以上两种情况下就放入阻塞队列。公平锁在以上条件下,还要加一个是否是在阻塞队列头部的判断,来维护其公平性。
state=0,没有线程获取锁,exclusiveOwnerThread=null;
state=1,有线程持有锁,exclusiveOwnerThread=该线程;
state>1,有线程重入锁
读写锁ReentrantReadWriteLock
读读不互斥,读写互斥,写写互斥
ReadWriteLock readWriteLock = new ReentrantReadWriteLock();Lock readLock = readWriteLock.readLock();readLock.lock();// 进行读取操作readLock.unlock();Lock writeLock = readWriteLock.writeLock();writeLock.lock();// 进行写操作writeLock.unlock();
表面上看readLock和writeLock是两把锁,实际上是一把锁的两个视图,什么是两个视图,可以理解成一把锁,基于AQS,维护同一个int型state变量,高16位表示读锁,低16位表示写锁,这样做是因为无法一个CAS操作操作两个int变量
Condition
使用场景:ArrayBlockingQueue 作为消息队列时,避免了notify和wait生产者通知生产者,消费者通知消费者的问题,因为生产者和消费者都是锁定的一个对象。
Condition必须和Lock一起用,Condition就在Lock里new出来,ConditionObject上维护了一个双向链表,链表里装的是线程,来维护线程的wait和唤醒
StampedLock
读读不互斥,读写不互斥,写写互斥。和读写锁的区别是采用了乐观读,读写锁采用了悲观读,会有一只读导致写线程饿死的情况。乐观读是读不加锁,读出来数据发现被修改了就加锁,降级为悲观读。
原理:乐观读是读之前获取数据版本号,通过对比版本号来判断数据是否被修改过,也是维护了一个state,低8位表示读写状态,第8位表示写状态,低7位表示读状态,这样写锁是不能重入的
