使用wait和notify

  1. import java.util.ArrayList;
  2. import java.util.LinkedList;
  3. import java.util.Queue;
  4. public class ThreadDemo {
  5. public static void main(String[] args) throws InterruptedException {
  6. var q = new TaskQueue();
  7. var ts = new ArrayList<Thread>();
  8. for (int i=0; i<5; i++) {
  9. var t = new Thread() {
  10. public void run() {
  11. // 执行task:
  12. while (true) {
  13. try {
  14. String s = q.getTask();
  15. System.out.println("execute task: " + s);
  16. } catch (InterruptedException e) {
  17. return;
  18. }
  19. }
  20. }
  21. };
  22. t.start();
  23. ts.add(t);
  24. }
  25. var add = new Thread(() -> {
  26. for (int i=0; i<10; i++) {
  27. // 放入task:
  28. String s = "t-" + Math.random();
  29. System.out.println("add task: " + s);
  30. q.addTask(s);
  31. try { Thread.sleep(100); } catch(InterruptedException e) {}
  32. }
  33. });
  34. add.start();
  35. add.join();
  36. Thread.sleep(100);
  37. for (var t : ts) {
  38. t.interrupt();
  39. }
  40. }
  41. }
  42. class TaskQueue {
  43. Queue<String> queue = new LinkedList<>();
  44. public synchronized void addTask(String s) {
  45. this.queue.add(s);
  46. this.notifyAll(); // 唤醒在this锁等待的进程
  47. /*
  48. * this.notify(); 唤醒一个
  49. * this.notifyAll(); 唤醒全部
  50. * */
  51. }
  52. public synchronized String getTask() throws InterruptedException {
  53. while (queue.isEmpty()) {
  54. // 释放锁
  55. this.wait();
  56. // 重新获得锁
  57. }
  58. return queue.remove(); // 取出列表中的第一个任务
  59. }
  60. }

ReentrantLock

  1. /*
  2. 我们知道Java语言直接提供了synchronized关键字用于加锁,但这种锁一是很重,二是获取时必须一直等待,没有额外的尝试机制。
  3. java.util.concurrent.locks包提供的ReentrantLock用于替代synchronized加锁*/
  4. // 传统的synchronized代码
  5. public class Counter {
  6. private int count;
  7. public void add(int n) {
  8. synchronized(this) {
  9. count += n;
  10. }
  11. }
  12. }
  13. // ReentrantLock上锁
  14. public class Counter {
  15. private final Lock lock = new ReentrantLock();
  16. private int count;
  17. public void add(int n) {
  18. lock.lock(); // 加锁
  19. try {
  20. count += n;
  21. } finally {
  22. lock.unlock(); // 释放锁
  23. }
  24. }
  25. }
  26. // 总结:使用ReentrantLock比直接使用synchronized更安全,线程在tryLock()失败的时候不会导致死锁。

使用Condition

Condition提供的await()signal()signalAll()原理和synchronized锁对象的wait()notify()notifyAll()是一致的,并且其行为也是一样的:

  • await()会释放当前锁,进入等待状态;
  • signal()会唤醒某个等待线程;
  • signalAll()会唤醒所有等待线程;
  • 唤醒线程从await()返回后需要重新获得锁。