使用wait和notify
import java.util.ArrayList;import java.util.LinkedList;import java.util.Queue;public class ThreadDemo {public static void main(String[] args) throws InterruptedException {var q = new TaskQueue();var ts = new ArrayList<Thread>();for (int i=0; i<5; i++) {var t = new Thread() {public void run() {// 执行task:while (true) {try {String s = q.getTask();System.out.println("execute task: " + s);} catch (InterruptedException e) {return;}}}};t.start();ts.add(t);}var add = new Thread(() -> {for (int i=0; i<10; i++) {// 放入task:String s = "t-" + Math.random();System.out.println("add task: " + s);q.addTask(s);try { Thread.sleep(100); } catch(InterruptedException e) {}}});add.start();add.join();Thread.sleep(100);for (var t : ts) {t.interrupt();}}}class TaskQueue {Queue<String> queue = new LinkedList<>();public synchronized void addTask(String s) {this.queue.add(s);this.notifyAll(); // 唤醒在this锁等待的进程/** this.notify(); 唤醒一个* this.notifyAll(); 唤醒全部* */}public synchronized String getTask() throws InterruptedException {while (queue.isEmpty()) {// 释放锁this.wait();// 重新获得锁}return queue.remove(); // 取出列表中的第一个任务}}
ReentrantLock
/*我们知道Java语言直接提供了synchronized关键字用于加锁,但这种锁一是很重,二是获取时必须一直等待,没有额外的尝试机制。java.util.concurrent.locks包提供的ReentrantLock用于替代synchronized加锁*/// 传统的synchronized代码public class Counter {private int count;public void add(int n) {synchronized(this) {count += n;}}}// ReentrantLock上锁public class Counter {private final Lock lock = new ReentrantLock();private int count;public void add(int n) {lock.lock(); // 加锁try {count += n;} finally {lock.unlock(); // 释放锁}}}// 总结:使用ReentrantLock比直接使用synchronized更安全,线程在tryLock()失败的时候不会导致死锁。
使用Condition
Condition提供的await()、signal()、signalAll()原理和synchronized锁对象的wait()、notify()、notifyAll()是一致的,并且其行为也是一样的:
await()会释放当前锁,进入等待状态;signal()会唤醒某个等待线程;signalAll()会唤醒所有等待线程;- 唤醒线程从
await()返回后需要重新获得锁。
