并发工具Lock & Condition
synchronized 和 Lock、Condition都是对管程的实现;为何在synchronized实现了管程后,Lock、Condition的是对管程的加强实现;synchronized申请资源的时候如果没有申请到就会进入阻塞队列也不会释放已有的资源;无法解决破坏不可抢占资源条件
相当对于synchronized解决的问题:
- 非阻塞的获取锁:获取锁失败直接返回,可以让获取锁失败的线程有机会释放之前获取的锁;
- 支持超时:通过超时参数使获取不到锁的线程不会一直阻塞等待;
- 能够响应中断:可以手动中断死锁的线程;
happen-before规则
- 顺序性规则
- volatile变量规则
- 传递性规则
可重入锁、公平锁和非公平锁
ReentrantLock顾名思义可以重复获取锁;其构造方法有一个控制是否是公平锁参数(fair),默认是非公平锁;
公平锁指的是等待时间最长的线程先获取锁,非公平锁则不能保证;
用锁的最佳实践
- 永远只在更新对象的成员变量时加锁
- 永远只在访问可变的成员变量时加锁
- 永远不在调用其他对象的方法时加锁
Doug Lea《Java 并发编程:设计原则与模式》
减少锁的持有时间、减小锁的粒度
那如何利用两个条件变量快速实现阻塞队列呢?
/**
* @author zhan
* @version 2021/6/22
*/
public class CustomerBlockQueue<T> {
Lock lock = new ReentrantLock();
Condition notFull;
Condition notEmpty;
public final List<T> container;
private int count=0;
private int size;
public CustomerBlockQueue(Integer size){
this.container = new ArrayList<>(size);
this.notEmpty = this.lock.newCondition();
this.notFull = this.lock.newCondition();
this.size = size;
}
public void enQueue(T node){
lock.lock();
try {
while (count==size){
notFull.await();
}
container.add(node);
count++;
notEmpty.signal();
}catch (Exception exception){
exception.printStackTrace();
}
finally {
lock.unlock();
}
}
public T deQueue(T node){
lock.lock();
try {
while (0 == container.size()){
notEmpty.await();
}
count--;
notFull.signal();
return container.get(0);
}catch (Exception exception){
exception.printStackTrace();
}finally {
lock.unlock();
}
return null;
}
}