java多线程(十)多把锁ReentrantLock

基于白嫖黑马程序员白嫖视频:全面深入学习java并发编程,java基础进阶中级必会教程

1 问题引入

synchronized关键字只能锁对象,就是说对象内部各个方法如果不是竞争状态的话,依然不能并行,一个对象只有一把锁,效率低。

2 活跃性

2.1 死锁

背景,当一个线程需要获得多把锁(占用多个资源)时,可能发生死锁情况;

2.2 哲学家就餐问题

5个哲学家,5只筷子,要么吃饭,要么思考,吃饭必须要2只筷子,当同时拿起右(左)手边的筷子时,就死锁了。

死锁发生的四个条件:

  • 互斥条件:一个资源每次只能被一个进程使用。
  • 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
  • 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
  • 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

2.3 活锁

两个线程互相改变对方的结束条件,导致最后谁也无法结束;比如一个水池对象,两个线程一个放水,一个加水,都是加满或者放完结束,结果一直一个加一个放。

2.4 饥饿

某个线程一直得不到CPU的执行调度,它非常饥饿。

3 ReentrantLock

比起synchronized更加灵活:

  • 可中断;
  • 可以设置超时时间;
  • 可以设置为公平锁;
  • 支持多个条件变量;

4 基本语法

reentrantLock.lock();
try {
    //临界区
}
finally {
    reentrantLock.unlock();
}

5 可重入

同一线程,因为自己已经获取锁了 ,如果后面的代码有继续获得该锁的,则有权利再次获得这把锁。reentrantLock是支持可重入的。

6 可打断

本线程在等待获得锁的过程中,别的线程可以中止我的等待;

reentrantLock.lockInterruptibly();//lock不可以被interrupt

当在等待锁的过程中被打断,马上进入catch块,但是isInterrupted依然是false,和synchronized一样。

7 锁超时

打断是被动,锁超时是主动的。

boolean getLock = reentrantLock.tryLock(1, TimeUnit.SECONDS);//1s后去尝试得到锁

8 公平锁

synchronized的Monitor管理的锁不是一个公平的锁(BLOCKED线程获得锁的机会不同);
ReentrantLock默认是非公平,但是可以设置构造函数,使得变成公平锁,即先到阻塞队列的线程先获得锁,后到的后获得。

ReentrantLock reentrantLock = new ReentrantLock(true);//公平锁

本意是解决饥饿问题的,但是太强调顺序了又影响并发性,失去了多线程设计的本意。

9 条件变量(Condition)

和对象的obj.wait()和obj.notify()一样,ReentrantLock也有类似的设置;

ReentrantLock reentrantLock = new ReentrantLock(true); // 公平锁
Condition condition =reentrantLock.newCondition();// 获得条件变量
reentrantLock.lock();//加锁
try {
    condition.await();//条件变量等待,会释放锁,此时可以被唤醒、设置超时、打断等等
} catch (InterruptedException e) {
    e.printStackTrace();
}
condition.signalAll();//通知所有的condition等待线程

版权声明:本文为weixin_44215363原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。