标签:读锁 获得 阻塞队列 可重入 使用 object类 countdown 查看 string
跟wait()不同!
引入了一个许可证的概念。
检测此时的线程是否拥有许可证,有的话。就通过,没有的话就阻塞。
LockSupport.park():在哪儿调用就是检查哪个线程
LockSupport.unpark(t):给t线程发一个许可证。
如果有许可证,直接返回
如果没有许可证,那就等一会再返回
讲blocker参数传入到阻塞线程中去
相对时间
准确时间
AbstractQueuedSynchronized:简称AQS,抽象同步队列;实现同步锁的基础组件。
并发包中的锁底层就是使用AQS实现的。
AQS是一个FIFO的双向队列,其内部通过节点head和tail记录队首和队尾元素,队列元素的类型是Node。
Node节点用来保存一条线程:
最终要的AQS中的state是实现不同锁的基础!
AQS中有内部类ConditionObject类:每个对象对应一个条件队列(单向链表队列):其中放调用条件变量await方法后被阻塞的线程。
对于ReentrantLock来说:state指的是可重入锁的次数
对于ReetrantReadWriteLock来说:高十六位是:读的重入数;低十六位:写锁的重入数;
对于CountDownlatch来说:state用来表示计数器当前的值
根据state是否属于一个线程:操作state的方式分为独占式和共享式。
谁获得了锁,state从0---->1;假如重入获取,state+1;解锁:state-1;
没有获取到锁:放入AQS阻塞队列中
boolean release(int args)
获取锁,没有就讲线程保存到AQS的阻塞队列里头。
例如信号量Semaphore:多个线程去请求资源时通过CAS方式竞争资源,当一个线程获取到了资源后,另外一个线程再次去获取时如果当前资源还能满足它的需要,则当前线程只需要使用CAS方式获取即可。
boolean releaseShared(int args)
void acquireShared(int arg):不响应中断
void acquireInterruptiblyShared(int arg):响应中断
----------------------------------------------------------------------------------------------------------------------------------------------------------------
获取锁:都是CAS
成功:执行
失败:LockSupport.park()假如到AQS队列里头等待
释放:释放成功,LockSupport.unpar()唤醒一个,CAS获取锁咯。
上述的tryxx的方法AQS需要实现的子类自己写
例如:ReentrantReadWriteLock
读锁重写tryAcquireShared时,首先查看写锁是否被其它线程持有,如果有则直接返回false,否则使用CAS递增state的高16位;
释放读锁时:重写tryRealeaseShared时,在内部需要使用CAS算法把当前state的高16位减1.
isHeldExclusively():判断锁是被当前线程独占还是被共享。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
就是之前的哪个ConditionObject类
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; public class ConditionTest { public static void main(String[] args) { ReentrantLock lock = new ReentrantLock(); Condition condition = lock.newCondition(); new Thread(new Runnable() { @Override public void run() { lock.lock(); System.out.println("I am one"); try { condition.await(); } catch (InterruptedException e) { e.printStackTrace(); }finally { System.out.println("I am one signal"); lock.unlock(); } } }).start(); new Thread(new Runnable() { @Override public void run() { lock.lock(); System.out.println("I am two"); condition.signal(); System.out.println("I am two"); lock.unlock(); } }).start(); } /** * I am one * I am two * I am two * I am one signal */ }
其实没什么难得,就是不在AQS的队列中等,在ConditionObject的队列中等。await和singal-------------》wait()和notify()差不多
当一个线程调用条件变量的signal方法时,会将条件队列的一个线程节点从条件队列中移到AQS的队列中,然后激活这个线程。
signalAll是将所有的线程放到AQS中
例如:生产者和消费者
有一个生产者A和两个消费者B.C
所以,在判断的时候用while,每次进来都判断一下。这样才能避免虚假唤醒。
重写tryAcquire(int acquires)、tryRelease(int releases)、newCondition()、isHeldExclusively()
假如是非公平锁:
公平锁
就是判断head指针的下一个节点是不是S,是的就冲了,不是就等等!,反正我是这么理解的
读写锁:维护了一个ReadLock和WriteLock
基于AQS实现
state高16位表示读状态
低16位标识写状态
如果当前没有线程获取到读锁和写锁,则当前线程可以获取到写锁然后返回。
如果当前线程有线程获取的读锁和写锁,写锁会被阻塞起来
重入+1
如果当前没有线程获得写锁,那就可以获取读锁
提供三种锁:
可以转换位写锁
不用加锁,只需要在使用的时候,判断一下有没有写锁!
标签:读锁 获得 阻塞队列 可重入 使用 object类 countdown 查看 string
原文地址:https://www.cnblogs.com/sicheng-li/p/13202563.html