Java并发包中锁原理剖析
2021-05-02 07:28
标签:读锁 获得 阻塞队列 可重入 使用 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类 其实没什么难得,就是不在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 如果当前没有线程获得写锁,那就可以获取读锁 提供三种锁: 可以转换位写锁 不用加锁,只需要在使用的时候,判断一下有没有写锁! Java并发包中锁原理剖析 标签:读锁 获得 阻塞队列 可重入 使用 object类 countdown 查看 string 原文地址:https://www.cnblogs.com/sicheng-li/p/13202563.html6.1LockSupport
void park()方法
void unpark(Thread thread)方法
LockSupport.parkNanos(long nanos)
LockSupport.park(Object blocker)方法
LockSupport.parkNanos(Object blocker, long nanos)
LockSupport.parkUnitl(Object blocker, long deadline)
大名鼎鼎的AQS
独占式
释放资源
获取资源
void acquire(int arg):不响应中断
void acquireInterruptibly(int arg):响应中断
共享式
释放资源
获取资源
成功:执行node节点入队操作
AQS---条件变量的支持
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的锁
独占锁ReentrantLock原理
获取锁lock()
tryLock()方法
释放锁 void unlock()方法
ReentrantReadWriteLock原理
WriteLock
ReadLock
StampedLock
写锁WriteLock
悲观读锁readLock
乐观读锁tryOptimisticRead