使用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()
返回后需要重新获得锁。