1. Condition的使用:
主要在Lock中配合Condition对象来实现wait和notify的功能
·
就像synchronized可以配合wait和notify实现线程在条件不满足时等待,条件满足时唤醒
1.1. 主要方法:
Condition提供的await()、signal()、signalAll()方法,就像synchronized锁对象的wait()、notify()、notifyAll()一样:
- await()会释放当前锁,进入等待状态;当然和wait()一样,最好在循环中使用
- signal()会唤醒某个等待线程;
- signalAll()会唤醒所有等待线程;
- 唤醒线程从await()返回后需要重新获得锁。
1.2. 举个栗子:
多线程循环打印ABC:
三个线程,线程A打印A,线程B打印B,线程C打印C,要求输出结果必须按 ABC 的顺序显示,如 ABCABCABC… 依次递推
- 线程A打印A,唤醒线程B,线程A阻塞
- 线程B打印B,唤醒线程C,线程B阻塞
- 线程C打印C,唤醒线程A,线程C阻塞
- 在Lock中通过await/signal来实现阻塞和唤醒
public class PrintABC {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Condition conditionA = lock.newCondition(); // 使用ReentrantLock的newCondition()方法创建Condition
Condition conditionB = lock.newCondition();
Condition conditionC = lock.newCondition();
// A线程
new Thread(() -> {
try {
lock.lock();
for (int i = 0; i < 10; i++) {
System.out.print(Thread.currentThread().getName());
conditionB.signal(); // 叫醒B线程
conditionA.await(); // 本线程阻塞
}
// 这里有个坑,要记得在循环之后调用signal(),否则线程可能会一直处于
// wait状态,导致程序无法结束
conditionB.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}, "A").start();
// B线程
new Thread(() -> {
try {
lock.lock();
for (int i = 0; i < 10; i++) {
System.out.print(Thread.currentThread().getName());
conditionC.signal();
conditionB.await();
}
conditionC.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}, "B").start();
// C线程
new Thread(() -> {
try {
lock.lock();
for (int i = 0; i < 10; i++) {
System.out.print(Thread.currentThread().getName());
conditionA.signal();
conditionC.await();
}
conditionA.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}, "C").start();
}
}
2. Semaphore(信号量)的使用:
Semaphore 是一个计数信号量,必须由获取它的线程释放。常用于限制某些资源可以访问的线程数量
2.1. 构造方法:
public Semaphore(int permits)
public Semaphore(int permits, boolean fair)
permits - 初始的可用许可数目
fair - 表示公平性,先进先出为true,反之false;这和new ReentrantLock(fasle);一样
2.2. 主要方法:
- acquire() 获取许可,并将许可数减1,许可数为0则阻塞
- release() 释放许可,并将许可数加1
- tryAcquire() 如果有可用的许可,返回true;反之false
2.3. 举个栗子:
多线程循环打印ABC:
三个线程,线程A打印A,线程B打印B,线程C打印C,要求输出结果必须按 ABC 的顺序显示,如 ABCABCABC… 依次递推
public class PrintABC {
public static void main(String[] args) {
// 初始化许可数为1,A线程可以先执行
Semaphore semaphoreA = new Semaphore(1);
// 初始化许可数为0,B线程阻塞
Semaphore semaphoreB = new Semaphore(0);
// 初始化许可数为0,C线程阻塞
Semaphore semaphoreC = new Semaphore(0);
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
// A线程获得许可,同时semaphoreA的许可数减为0,进入下一次循环时
// A线程会阻塞,知道其他线程执行semaphoreA.release();
semaphoreA.acquire();
// 打印当前线程名称
System.out.print(Thread.currentThread().getName());
// semaphoreB许可数加1
semaphoreB.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
semaphoreB.acquire();
System.out.print(Thread.currentThread().getName());
semaphoreC.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
semaphoreC.acquire();
System.out.print(Thread.currentThread().getName());
semaphoreA.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "C").start();
}
}
【参考资料】:https://segmentfault.com/a/1190000021433079