Lock
公平锁
所谓公平锁就是按照申请锁的顺序来执行,就像排队取款一样,前面的人取款结束后面的人才可以取,就是一个队列。
非公平锁
非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,与上面相反有可能后申请锁的反而先获得了锁导致优先级翻转;也会出现某个线程一直拿不到锁导致线程饥饿现象。
非公平锁的优点在于吞吐量比公平锁大,synchronized
就是一种非公平锁
锁的使用ReentrantLock
ReentrantLock实现了Lock接口,使用时他有无参构造方法和有参构造方法。
无参构造放从源码可以看出是调用的NonfairSync()
方法,这是一个非公平锁从名字就可以看出。
/**
* Creates an instance of {@code ReentrantLock}.
* This is equivalent to using {@code ReentrantLock(false)}.
*/
public ReentrantLock() {
sync = new NonfairSync();
}
有参构造方法是接收一个boolean
类型变量。从代码可以看出传true
时实例化的是公平锁,传false
时实例化的是非公平锁。
/**
* Creates an instance of {@code ReentrantLock} with the
* given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
Condition
Condition
里的await()
、signal()
、signalAll()
与Object
监视器方法里的( wait()
, notify()
和notifyAll()
)类似。Lock
可替换synchronized
方法和语句的使用, Condition
取代了对象监视器方法的使用。
Condition的常用方法
方法 | 描述 |
---|---|
await() | 使当前线程进入挂起等待状态,等待信号继续进行。 |
signal() | 唤醒一个等待线程。 |
signalAll() | 唤醒所有等待线程。 |
Lock绑定多个Condition使用
这里做一个链式唤醒的操作,流程图如下
锁的三部曲: 判断 干活 唤醒
里面的判断为了避免虚假唤醒,一定要采用 while
案例: 做个公司招人面试的流程
- 来个一个求职者hr先面试问几个问题
- manager再面试问几个问题
- boss面试问几个问题,没有通过告知hr继续招人
此时三个面试官分别是三个线程,按照顺序来。模拟面试6个求求职者。(不要纠结为啥都没通过,只是演示)
创建一个标识
// 标志位 0:hr 1:manager 2:boss
private int flag = 0;
创建一把重入锁
// 创建一个重入锁
private Lock lock = new ReentrantLock();
给这把锁创建三把钥匙
// 这三个相当于备用钥匙
Condition c1 = lock.newCondition();
Condition c2 = lock.newCondition();
Condition c3 = lock.newCondition();
- 判断 判断标识位是否成立否则就进入挂起等待状态
- 干活 面试官提问问题
- 通知 前一个面试官面试完通知下一个面试官
求职业务类完整代码Interview
package com.avatar.juc.sample1;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 面试需要的流程
* 1、hr 提问三个问题
* 2、manager 提问五个问题
* 3、boss 提问两个问题
* 微信公众号:快乐学习与分享
*/
public class Interview {
// 标志位 0:hr 1:manager 2:boss
private int flag = 0;
// 创建一个重入锁
private Lock lock = new ReentrantLock();
// 这三个相当于备用钥匙
Condition c1 = lock.newCondition();
Condition c2 = lock.newCondition();
Condition c3 = lock.newCondition();
/**
* hr 面试
* @param num 面试者序号
*/
public void hrAsk3(int num){
lock.lock();
try {
// 1、判断flag不等于0是挂起
while (flag != 0){ // 为了避免虚假唤醒,一定要采用 while
c1.await();
}
System.out.println(Thread.currentThread().getName() + "\t面试第:" + num + "位面试者");
// 2、工作 hr提问
for (int i = 1; i <= 3; i++) {
System.out.println(Thread.currentThread().getName() + "\t提问第:" + i + "个问题");
}
// 3、通知 进入下个流程
flag = 1;
c2.signal();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
/**
* manger 面试
* @param num 面试者序号
*/
public void mangerAsk5(int num){
lock.lock();
try {
// 1、判断flag不等于1是挂起
while (flag != 1){ // 为了避免虚假唤醒,一定要采用 while
c2.await();
}
System.out.println(Thread.currentThread().getName() + "\t面试第:" + num + "位面试者");
// 2、工作 manger 提问
for (int i = 1; i <= 5; i++) {
System.out.println(Thread.currentThread().getName() + "\t提问第:" + i + "个问题");
}
// 3、通知 进入下个流程
flag = 2;
c3.signal();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
/**
* boss 面试
* @param num 面试者序号
*/
public void bossAsk3(int num){
lock.lock();
try {
// 1、判断flag不等于2是挂起
while (flag != 2){ // 为了避免虚假唤醒,一定要采用 while
c3.await();
}
System.out.println(Thread.currentThread().getName() + "\t面试第:" + num + "位面试者");
// 2、工作 hr提问
for (int i = 1; i <= 2; i++) {
System.out.println(Thread.currentThread().getName() + "\t提问第:" + i + "个问题");
}
System.out.println("很可惜,没通过。。。");
System.out.println("");
// 3、通知 进入下个流程
flag = 0;
c1.signal();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
模拟6个人面试测试类完整代码LockAndConditionDemo
package com.avatar.juc.sample1;
/**
* 微信公众号:快乐学习与分享
*
* Lock锁绑定多个条件condition
*
* 案例:面试者面试流程为例,一个面试者来到
* hr 先面试 提问3个问题 -> manager 再面试提问5个问题 -> boss最后面试提问2个问题
* hr 先面试 提问3个问题 -> manager 再面试提问5个问题 -> boss最后面试提问2个问题
* hr 先面试 提问3个问题 -> manager 再面试提问5个问题 -> boss最后面试提问2个问题
* 。。。。。。
*
* 假如共面试6个人(案例只为演示)
*/
public class LockAndConditionDemo {
public static void main(String[] args) {
Interview interview = new Interview();
new Thread(()->{
for (int i = 1; i <= 6; i++) {
interview.hrAsk3(i);
}
},"hr").start();
new Thread(()->{
for (int i = 1; i <= 6; i++) {
interview.mangerAsk5(i);
}
},"manager").start();
new Thread(()->{
for (int i = 1; i <= 6; i++) {
interview.bossAsk3(i);
}
},"boss").start();
}
}
测试打印结果
hr 面试第:1位面试者
hr 提问第:1个问题
hr 提问第:2个问题
hr 提问第:3个问题
manager 面试第:1位面试者
manager 提问第:1个问题
manager 提问第:2个问题
manager 提问第:3个问题
manager 提问第:4个问题
manager 提问第:5个问题
boss 面试第:1位面试者
boss 提问第:1个问题
boss 提问第:2个问题
没通过。。。
hr 面试第:2位面试者
hr 提问第:1个问题
hr 提问第:2个问题
hr 提问第:3个问题
manager 面试第:2位面试者
manager 提问第:1个问题
manager 提问第:2个问题
manager 提问第:3个问题
manager 提问第:4个问题
manager 提问第:5个问题
boss 面试第:2位面试者
boss 提问第:1个问题
boss 提问第:2个问题
没通过。。。
......
了解更多: