Lock
Lock继synchronized出现的原因
synchronized无法实现破坏不可抢占锁的功能,synchronized申请资源申请不到的时候,直接进入阻塞状态,啥都干不了,也释放不了线程占有的资源;
所以为了解决这个问题,出现了Java SDK——Lock;Lock接口的三个方法如下:
// 支持中断的API// 能够响应中断。阻塞状态的线程能够响应中断信号,能够唤醒它(那么就有可能释放持有的锁)void lockInterruptibly() throws InterruptedException;// 支持超时的API// 一段时间内没有获取到锁,不是进入阻塞状态,而是返回错误boolean tryLock(long time, TimeUnit unit) throws InterruptedException;// 支持非阻塞获取锁的API// 尝试获取锁失败,不进入阻塞状态直接返回boolean tryLock();
如何保证可见性
synchronized之所以能保证可见性是因为Happens-Before中一条规则,”管程中锁的规则。对一个锁的解锁Happens-Before于后续对这个锁的加锁“;
Java SDK里面的锁主要是利用violate相关的Happens-Before规则(顺序性、volatile变量规则、传递性规则),例子如下:
class SampleLock {volatile int state;// 加锁lock() {// 省略代码无数state = 1;}// 解锁unlock() {// 省略代码无数state = 0;}}
可重入锁
ReentrantLock,线程可以重复获取同一把锁;除了可重入锁还有可重入函数,可重入函数指得是多个线程同时调用该函数,每个线程都能得到正确结果;同时在一个线程内支持线程切换。其实就是意味着线程安全;
公平锁和非公平锁
ReentrantLock 这个类有两个构造函数,一个是无参构造函数,一个是传入 fair 参数的构造函数。
//无参构造函数:默认非公平锁public ReentrantLock() {sync = new NonfairSync();}//根据公平策略参数创建锁public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();}
实战示例
利用两个条件变量快速实现阻塞队列
public class BlockedQueue<T>{final Lock lock = new ReentrantLock();// 条件变量:队列不满final Condition notFull = lock.newCondition();// 条件变量:队列不空final Condition notEmpty = lock.newCondition();// 入队void enq(T x) {lock.lock();try {while (队列已满){// 等待队列不满notFull.await();}// 省略入队操作...//入队后,通知可出队notEmpty.signal();} finally {lock.unlock();}}// 出队void deq(){lock.lock();try {while (队列已空){// 等待队列不空notEmpty.await();}// 省略出队操作...//出队后,通知可入队notFull.signal();} finally {lock.unlock();}}}
