1.什么是JUC?

JUC是指java.Util包中的三个操作线程的包!
java.util.concurrent
java.util.concurrent.atomic
java.util.concurrent.locks
image.png

2.线程进程和程序

程序(Program):是一个静态的概念,一般对应于操作系统中的一个可执行的文件。
比如:我们要启动酷狗听音乐,则对应酷狗可执行程序。当我们双击酷狗,则加载程序到内存中,开始执行该程序,于是产生了“进程”。
进程:执行中的程序叫做进程(Process),是一个动态的概念。现代的操作系统都可以同时启动多个进程。
比如:我们在用酷狗听音乐,也可以使用wps写文档,也可以同时用浏览器查看网页。可以通过任务管理器查看当前的进程。
线程:是进程的一个执行路径,一个进程中至少有一个线程,进程中的多个线程共享进程的资源。
java默认有两个线程:main(主)线程和GC(垃圾回收)线程

java真的可以开启线程吗?开启不了!

  1. public synchronized void start() {
  2. /**
  3. * This method is not invoked for the main method thread or "system"
  4. * group threads created/set up by the VM. Any new functionality added
  5. * to this method in the future may have to also be added to the VM.
  6. *
  7. * A zero status value corresponds to state "NEW".
  8. */
  9. if (threadStatus != 0)
  10. throw new IllegalThreadStateException();
  11. /* Notify the group that this thread is about to be started
  12. * so that it can be added to the group's list of threads
  13. * and the group's unstarted count can be decremented. */
  14. group.add(this);
  15. boolean started = false;
  16. try {
  17. start0();
  18. started = true;
  19. } finally {
  20. try {
  21. if (!started) {
  22. group.threadStartFailed(this);
  23. }
  24. } catch (Throwable ignore) {
  25. /* do nothing. If start0 threw a Throwable then
  26. it will be passed up the call stack */
  27. }
  28. }
  29. }
  30. //这是一个C++底层,Java是没有权限操作底层硬件的
  31. private native void start0();

Java是没有权限去开启线程、操作硬件的,这是一个native的一个本地方法,它调用的底层的C++代码。
并发: 多线程操作同一个资源。
CPU 只有一核,模拟出来多条线程,天下武功,唯快不破。那么我们就可以使用CPU快速交替,来模拟多线程。
并行: 多个人(CPU内核)一起行走(运行)
CPU多核,多个线程可以同时执行。 我们可以使用线程池!

  1. public class Test1 {
  2. public static void main(String[] args) {
  3. //获取cpu的核数
  4. System.out.println(Runtime.getRuntime().availableProcessors());
  5. }
  6. }

线程有几个状态?6个

  1. public enum State {
  2. /**
  3. * Thread state for a thread which has not yet started.
  4. */
  5. //运行(新生,就绪)
  6. NEW,
  7. /**
  8. * Thread state for a runnable thread. A thread in the runnable
  9. * state is executing in the Java virtual machine but it may
  10. * be waiting for other resources from the operating system
  11. * such as processor.
  12. */
  13. //运行
  14. RUNNABLE,
  15. /**
  16. * Thread state for a thread blocked waiting for a monitor lock.
  17. * A thread in the blocked state is waiting for a monitor lock
  18. * to enter a synchronized block/method or
  19. * reenter a synchronized block/method after calling
  20. * {@link Object#wait() Object.wait}.
  21. */
  22. //阻塞
  23. BLOCKED,
  24. /**
  25. * Thread state for a waiting thread.
  26. * A thread is in the waiting state due to calling one of the
  27. * following methods:
  28. * <ul>
  29. * <li>{@link Object#wait() Object.wait} with no timeout</li>
  30. * <li>{@link #join() Thread.join} with no timeout</li>
  31. * <li>{@link LockSupport#park() LockSupport.park}</li>
  32. * </ul>
  33. *
  34. * <p>A thread in the waiting state is waiting for another thread to
  35. * perform a particular action.
  36. *
  37. * For example, a thread that has called <tt>Object.wait()</tt>
  38. * on an object is waiting for another thread to call
  39. * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
  40. * that object. A thread that has called <tt>Thread.join()</tt>
  41. * is waiting for a specified thread to terminate.
  42. */
  43. //等待
  44. WAITING,
  45. /**
  46. * Thread state for a waiting thread with a specified waiting time.
  47. * A thread is in the timed waiting state due to calling one of
  48. * the following methods with a specified positive waiting time:
  49. * <ul>
  50. * <li>{@link #sleep Thread.sleep}</li>
  51. * <li>{@link Object#wait(long) Object.wait} with timeout</li>
  52. * <li>{@link #join(long) Thread.join} with timeout</li>
  53. * <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
  54. * <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
  55. * </ul>
  56. */
  57. //超时等待
  58. TIMED_WAITING,
  59. /**
  60. * Thread state for a terminated thread.
  61. * The thread has completed execution.
  62. */
  63. //终止
  64. TERMINATED;
  65. }

wait/sleep的区别?

1.来自不同的类

wait => Object
sleep => Thread
一般情况企业中使用休眠是:
TimeUnit.DAYS.sleep(1); //休眠1天
TimeUnit.SECONDS.sleep(1); //休眠1s

2.关于锁的释放

wait 释放锁睡;
sleep 抱着锁睡;

3.使用的范围是不同的

wait 必须在同步代码块中;
sleep 可以在任何地方睡;

4.是否需要捕获异常

wait是不需要捕获异常(中断异常除外,所有线程都有中断异常);
sleep必须要捕获异常;

3.Lock锁(重点)

传统:synchronized

  1. package com.test.demo;
  2. /**
  3. * 基本的卖票例子
  4. * 记住:线程就是一个单独的资源类,没用任何的附属操作!
  5. */
  6. public class Demo01{
  7. public static void main(String[] args) {
  8. //多线程操作
  9. Ticket ticket = new Ticket();
  10. new Thread(()-> { for (int i = 0; i < 60; i++) ticket.sale(); },"A").start();
  11. new Thread(()-> { for (int i = 0; i < 60; i++) ticket.sale(); },"B").start();
  12. new Thread(()-> { for (int i = 0; i < 60; i++) ticket.sale(); },"C").start();
  13. new Thread(()-> { for (int i = 0; i < 60; i++) ticket.sale(); },"D").start();
  14. }
  15. }
  16. //资源类
  17. class Ticket{
  18. private static int number=50;
  19. //卖票方式
  20. public synchronized void sale(){
  21. if (number>0){
  22. System.out.println(Thread.currentThread().getName() + "购买了第" + (number--) + "张票,剩余票数为"+number);
  23. }
  24. }
  25. }

Lock接口

image.png
image.png
image.png

公平锁:十分公平,必须先来后到~

非公平锁:十分不公平,可以插队(默认为非公平锁)。

  1. package com.test.demo;
  2. import java.util.concurrent.locks.Lock;
  3. import java.util.concurrent.locks.ReentrantLock;
  4. /**
  5. * 基本的卖票例子
  6. * 记住:线程就是一个单独的资源类,没用任何的附属操作!
  7. */
  8. public class Demo01{
  9. public static void main(String[] args) {
  10. //多线程操作
  11. Ticket ticket = new Ticket();
  12. new Thread(()-> { for (int i = 0; i < 60; i++) ticket.sale(); },"A").start();
  13. new Thread(()-> { for (int i = 0; i < 60; i++) ticket.sale(); },"B").start();
  14. new Thread(()-> { for (int i = 0; i < 60; i++) ticket.sale(); },"C").start();
  15. new Thread(()-> { for (int i = 0; i < 60; i++) ticket.sale(); },"D").start();
  16. }
  17. }
  18. //资源类
  19. class Ticket{
  20. private static int number=50;
  21. Lock lock= new ReentrantLock();
  22. //卖票方式
  23. public void sale(){
  24. //加锁
  25. lock.lock();
  26. try {
  27. if (number>0){
  28. System.out.println(Thread.currentThread().getName() + "购买了第" + (number--) + "张票,剩余票数为"+number);
  29. }
  30. } finally {
  31. //解锁
  32. lock.unlock();
  33. }
  34. }
  35. }

synchronized锁与Lock锁的区别

  1. synchronized是内置的java关键字而Lock是一个接口。
  2. synchronized无法判断获取锁的状态,Lock可以判断是否获取到了锁。
  3. synchronized会自动释放锁,Lock必须要手动释放锁!否则会造成死锁。
  4. synchronized 线程1(获得锁,阻塞),线程2(等待,傻傻的等);Lock锁就不会一直等下去。
  5. synchronized 可重入锁 不可以中断的 非公平,Lock 可重入锁 可以判断的 非公平(可以设置)。
  6. synchronized 适合锁少量同步代码,Lock适合锁大量同步代码。

    锁是什么?如何判断锁的是谁?

    个人理解:锁是一种用于解决安全的机制,java中锁一般都是锁的对象,或者锁的是需要进行增删改的属性或发方法。

    4.生产者和消费者问题

    synchronized版

  1. package JUC.PC;
  2. public class A {
  3. public static void main(String[] args) {
  4. Data data = new Data();
  5. new Thread(()->{
  6. try {
  7. for (int i = 0; i < 20; i++) {
  8. data.Production();
  9. }
  10. } catch (InterruptedException e) {
  11. e.printStackTrace();
  12. }
  13. },"A").start();
  14. new Thread(()->{
  15. try {
  16. for (int i = 0; i < 20; i++) {
  17. data.Consumption();
  18. }
  19. } catch (InterruptedException e) {
  20. e.printStackTrace();
  21. }
  22. },"B").start();
  23. }
  24. }
  25. //资源类
  26. class Data {
  27. private int number = 0;
  28. //生产
  29. public synchronized void Production() throws InterruptedException {
  30. if (number != 0) {
  31. this.wait();
  32. }
  33. number++;
  34. System.out.println(Thread.currentThread().getName()+"线程生产+1,目前总数"+number);
  35. this.notifyAll();
  36. }
  37. //消费
  38. public synchronized void Consumption() throws InterruptedException {
  39. if (number==0){
  40. this.wait();
  41. }
  42. number--;
  43. System.out.println(Thread.currentThread().getName()+"线程消费-1,目前总数"+number);
  44. this.notifyAll();
  45. }
  46. }

如果有ABCD4个线程则会出现问题?虚假唤醒!
image.png
解决方案:将if改为while即可

  1. package JUC.PC;
  2. public class A {
  3. public static void main(String[] args) {
  4. Data data = new Data();
  5. new Thread(() -> {
  6. try {
  7. for (int i = 0; i < 20; i++) {
  8. data.Production();
  9. }
  10. } catch (InterruptedException e) {
  11. e.printStackTrace();
  12. }
  13. }, "A").start();
  14. new Thread(() -> {
  15. try {
  16. for (int i = 0; i < 20; i++) {
  17. data.Consumption();
  18. }
  19. } catch (InterruptedException e) {
  20. e.printStackTrace();
  21. }
  22. }, "B").start();
  23. new Thread(() -> {
  24. try {
  25. for (int i = 0; i < 20; i++) {
  26. data.Production();
  27. }
  28. } catch (InterruptedException e) {
  29. e.printStackTrace();
  30. }
  31. }, "C").start();
  32. new Thread(() -> {
  33. try {
  34. for (int i = 0; i < 20; i++) {
  35. data.Consumption();
  36. }
  37. } catch (InterruptedException e) {
  38. e.printStackTrace();
  39. }
  40. }, "D").start();
  41. }
  42. }
  43. //资源类
  44. class Data {
  45. private int number = 0;
  46. //生产
  47. public synchronized void Production() throws InterruptedException {
  48. while (number != 0) {
  49. this.wait();
  50. }
  51. number++;
  52. System.out.println(Thread.currentThread().getName() + "线程生产+1,目前总数" + number);
  53. this.notifyAll();
  54. }
  55. //消费
  56. public synchronized void Consumption() throws InterruptedException {
  57. while (number == 0) {
  58. this.wait();
  59. }
  60. number--;
  61. System.out.println(Thread.currentThread().getName() + "线程消费-1,目前总数" + number);
  62. this.notifyAll();
  63. }
  64. }

JUC版的生产者和消费者问题

image.png
image.png

  1. package JUC.PC;
  2. import java.lang.management.LockInfo;
  3. import java.util.concurrent.locks.Condition;
  4. import java.util.concurrent.locks.Lock;
  5. import java.util.concurrent.locks.ReentrantLock;
  6. public class A {
  7. public static void main(String[] args) {
  8. Data data = new Data();
  9. new Thread(() -> {
  10. try {
  11. for (int i = 0; i < 20; i++) {
  12. data.Production();
  13. }
  14. } catch (InterruptedException e) {
  15. e.printStackTrace();
  16. }
  17. }, "A").start();
  18. new Thread(() -> {
  19. try {
  20. for (int i = 0; i < 20; i++) {
  21. data.Consumption();
  22. }
  23. } catch (InterruptedException e) {
  24. e.printStackTrace();
  25. }
  26. }, "B").start();
  27. new Thread(() -> {
  28. try {
  29. for (int i = 0; i < 20; i++) {
  30. data.Production();
  31. }
  32. } catch (InterruptedException e) {
  33. e.printStackTrace();
  34. }
  35. }, "C").start();
  36. new Thread(() -> {
  37. try {
  38. for (int i = 0; i < 20; i++) {
  39. data.Consumption();
  40. }
  41. } catch (InterruptedException e) {
  42. e.printStackTrace();
  43. }
  44. }, "D").start();
  45. }
  46. }
  47. //资源类
  48. class Data {
  49. private int number = 0;
  50. Lock reentrantLock = new ReentrantLock();
  51. Condition condition = reentrantLock.newCondition();
  52. //生产
  53. public void Production() throws InterruptedException {
  54. try {
  55. reentrantLock.lock();
  56. while (number != 0) {
  57. condition.await();
  58. }
  59. number++;
  60. System.out.println(Thread.currentThread().getName() + "线程生产+1,目前总数" + number);
  61. condition.signalAll();
  62. } finally {
  63. reentrantLock.unlock();
  64. }
  65. }
  66. //消费
  67. public void Consumption() throws InterruptedException {
  68. try {
  69. reentrantLock.lock();
  70. while (number == 0) {
  71. condition.await();
  72. }
  73. number--;
  74. System.out.println(Thread.currentThread().getName() + "线程消费-1,目前总数" + number);
  75. condition.signalAll();
  76. } finally {
  77. reentrantLock.unlock();
  78. }
  79. }
  80. }

任何一个新技术,绝不仅仅只是覆盖了原来的技术,一定有它的优势和补充了原来的技术!
Condition 精准通知和唤醒线程
image.png
可以定义多个监视器进行精准通知,如:

  1. package JUC.PC;
  2. import java.util.concurrent.locks.Condition;
  3. import java.util.concurrent.locks.Lock;
  4. import java.util.concurrent.locks.ReentrantLock;
  5. public class B {
  6. public static void main(String[] args) {
  7. Data01 data01 = new Data01();
  8. new Thread(() -> {
  9. for (int i = 0; i < 10; i++) {
  10. data01.printA();
  11. }
  12. }, "A").start();
  13. new Thread(() -> {
  14. for (int i = 0; i < 10; i++) {
  15. data01.printB();
  16. }
  17. }, "B").start();
  18. new Thread(() -> {
  19. for (int i = 0; i < 10; i++) {
  20. data01.printC();
  21. }
  22. }, "C").start();
  23. new Thread(() -> {
  24. for (int i = 0; i < 10; i++) {
  25. data01.printD();
  26. }
  27. }, "D").start();
  28. }
  29. }
  30. class Data01 {
  31. //可重入锁
  32. private final Lock lock = new ReentrantLock();
  33. //监视器
  34. private final Condition condition1 = lock.newCondition();
  35. private final Condition condition2 = lock.newCondition();
  36. private final Condition condition3 = lock.newCondition();
  37. private final Condition condition4 = lock.newCondition();
  38. //物品数量
  39. private int count = 0;
  40. //生产
  41. public void printA() {
  42. lock.lock();
  43. try {
  44. while (count != 0) {
  45. //等待
  46. condition1.await();
  47. }
  48. count++;
  49. System.out.println(Thread.currentThread().getName() + "生产物品数量+1物品还剩" + count + "个,通知B消费");
  50. //唤醒B
  51. condition2.signal();
  52. } catch (InterruptedException e) {
  53. e.printStackTrace();
  54. } finally {
  55. lock.unlock();
  56. }
  57. }
  58. //消费
  59. public void printB() {
  60. lock.lock();
  61. try {
  62. while (count <= 0) {
  63. condition2.await();
  64. }
  65. count--;
  66. System.out.println(Thread.currentThread().getName() + "消费物品数量-1物品还剩" + count + "个,通知C生产");
  67. //通知C
  68. condition3.signal();
  69. } catch (Exception e) {
  70. e.printStackTrace();
  71. } finally {
  72. lock.unlock();
  73. }
  74. }
  75. //生产
  76. public void printC() {
  77. try {
  78. lock.lock();
  79. while (count != 0) {
  80. //等待
  81. condition3.await();
  82. }
  83. count++;
  84. System.out.println(Thread.currentThread().getName() + "生产物品数量+1物品还剩" + count + "个,通知D消费");
  85. //唤醒D
  86. condition4.signal();
  87. } catch (InterruptedException e) {
  88. e.printStackTrace();
  89. } finally {
  90. lock.unlock();
  91. }
  92. }
  93. //消费
  94. public void printD() {
  95. lock.lock();
  96. try {
  97. while (count <= 0) {
  98. condition4.await();
  99. }
  100. count--;
  101. System.out.println(Thread.currentThread().getName() + "消费物品数量-1物品还剩" + count + "个,通知A生产");
  102. //通知A
  103. condition1.signal();
  104. } catch (Exception e) {
  105. e.printStackTrace();
  106. } finally {
  107. lock.unlock();
  108. }
  109. }
  110. }

5.八锁现象

如何判断锁的是什么?

锁对象或class对象!

问题一:在标准情况下,两个线程是先发短信还是先打电话?

  1. package JUC.Lock8;
  2. import java.util.concurrent.TimeUnit;
  3. /**
  4. * 8锁,就是关于锁的八个问题
  5. * 1.在标准情况下,两个线程是先发短信还是先打电话? 先发短信
  6. */
  7. public class Test1 {
  8. public static void main(String[] args) throws InterruptedException {
  9. Phone phone = new Phone();
  10. new Thread(() -> {
  11. phone.sendSms();
  12. }, "A").start();
  13. TimeUnit.SECONDS.sleep(1);
  14. new Thread(() -> {
  15. phone.call();
  16. }, "B").start();
  17. }
  18. }
  19. class Phone {
  20. //发短信
  21. public synchronized void sendSms() {
  22. System.out.println("发短信中...");
  23. }
  24. //打电话
  25. public synchronized void call() {
  26. System.out.println("打电话中...");
  27. }
  28. }

问题二:在sendSms方法延迟了4秒后的情况下,两个线程是先发短信还是先打电话?

  1. package JUC.Lock8;
  2. import java.util.concurrent.TimeUnit;
  3. /**
  4. * 8锁,就是关于锁的八个问题
  5. * 1.在标准情况下,两个线程是先发短信还是先打电话? 先发短信
  6. * 2.在sendSms方法延迟了4秒后的情况下,两个线程是先发短信还是先打电话? 先发短信,因为两个方法都是同一个对象所以代表着谁先拿到锁谁就先执行
  7. */
  8. public class Test1 {
  9. public static void main(String[] args) throws InterruptedException {
  10. Phone phone = new Phone();
  11. new Thread(() -> {
  12. try {
  13. phone.sendSms();
  14. } catch (InterruptedException e) {
  15. e.printStackTrace();
  16. }
  17. }, "A").start();
  18. TimeUnit.SECONDS.sleep(1);
  19. new Thread(() -> {
  20. phone.call();
  21. }, "B").start();
  22. }
  23. }
  24. class Phone {
  25. //发短信
  26. public synchronized void sendSms() throws InterruptedException {
  27. TimeUnit.SECONDS.sleep(4);
  28. System.out.println("发短信中...");
  29. }
  30. //打电话
  31. public synchronized void call() {
  32. System.out.println("打电话中...");
  33. }
  34. }

问题三:这里输出发短信还是hello?

  1. package JUC.Lock8;
  2. import java.util.concurrent.TimeUnit;
  3. /**
  4. * 8锁,就是关于锁的八个问题
  5. * 1.在标准情况下,两个线程是先发短信还是先打电话? 先发短信
  6. * 2.在sendSms方法延迟了4秒后的情况下,两个线程是先发短信还是先打电话? 先发短信,因为两个方法都是同一个对象所以代表着谁先拿到锁谁就先执行
  7. * 3.这里输出发短信还是hello? hello 因为着hello这个方法并没有上锁,且发短信的方法以及拉到了锁但是需要睡眠4秒所以是hello
  8. */
  9. public class Test1 {
  10. public static void main(String[] args) throws InterruptedException {
  11. Phone phone = new Phone();
  12. new Thread(() -> {
  13. try {
  14. phone.sendSms();
  15. } catch (InterruptedException e) {
  16. e.printStackTrace();
  17. }
  18. }, "A").start();
  19. TimeUnit.SECONDS.sleep(1);
  20. new Thread(() -> {
  21. phone.hello();
  22. }, "B").start();
  23. }
  24. }
  25. class Phone {
  26. //发短信
  27. public synchronized void sendSms() throws InterruptedException {
  28. TimeUnit.SECONDS.sleep(4);
  29. System.out.println("发短信中...");
  30. }
  31. //打电话
  32. public synchronized void call() {
  33. System.out.println("打电话中...");
  34. }
  35. public void hello(){
  36. System.out.println("hello");
  37. }
  38. }

问题四:有两个对象先执行发短信还是打电话?

  1. package JUC.Lock8;
  2. import java.util.concurrent.TimeUnit;
  3. /**
  4. * 8锁,就是关于锁的八个问题
  5. * 1.在标准情况下,两个线程是先发短信还是先打电话? 先发短信
  6. * 2.在sendSms方法延迟了4秒后的情况下,两个线程是先发短信还是先打电话? 先发短信,因为两个方法都是同一个对象所以代表着谁先拿到锁谁就先执行
  7. * 3.这里输出发短信还是hello? hello 因为着hello这个方法并没有上锁,且发短信的方法以及拉到了锁但是需要睡眠4秒所以是hello
  8. * 4.有两个对象先执行发短信还是打电话? 打电话,应为这里有了两个对象将不在受锁的影响,而sendSms方法中又睡眠的4秒所以是先打电话
  9. */
  10. public class Test1 {
  11. public static void main(String[] args) throws InterruptedException {
  12. //两个对象
  13. Phone phone = new Phone();
  14. Phone phone01 = new Phone();
  15. new Thread(() -> {
  16. try {
  17. phone.sendSms();
  18. } catch (InterruptedException e) {
  19. e.printStackTrace();
  20. }
  21. }, "A").start();
  22. TimeUnit.SECONDS.sleep(1);
  23. new Thread(() -> {
  24. phone01.call();
  25. }, "B").start();
  26. }
  27. }
  28. class Phone {
  29. //发短信
  30. public synchronized void sendSms() throws InterruptedException {
  31. TimeUnit.SECONDS.sleep(4);
  32. System.out.println("发短信中...");
  33. }
  34. //打电话
  35. public synchronized void call() {
  36. System.out.println("打电话中...");
  37. }
  38. }

问题五:增加两个静态同步方法,只有一个对象,先发短信还是先打电话?

  1. package JUC.Lock8;
  2. import java.util.concurrent.TimeUnit;
  3. /**
  4. * 8锁,就是关于锁的八个问题
  5. * 5.增加两个静态同步方法,只有一个对象,先发短信还是先打电话? 发短信,因为静态的方法从属于类则锁锁的就是类了,而sendSms短信是最先拿到锁的
  6. */
  7. public class Test2 {
  8. public static void main(String[] args) throws InterruptedException {
  9. //两个对象
  10. Phone phone = new Phone();
  11. new Thread(() -> {
  12. try {
  13. phone.sendSms();
  14. } catch (InterruptedException e) {
  15. e.printStackTrace();
  16. }
  17. }, "A").start();
  18. TimeUnit.SECONDS.sleep(1);
  19. new Thread(() -> {
  20. phone.call();
  21. }, "B").start();
  22. }
  23. }
  24. class Phone {
  25. //发短信
  26. public static synchronized void sendSms() throws InterruptedException {
  27. TimeUnit.SECONDS.sleep(4);
  28. System.out.println("发短信中...");
  29. }
  30. //打电话
  31. public static synchronized void call() {
  32. System.out.println("打电话中...");
  33. }
  34. }

问题六:增加两个静态同步方法,两个个对象,先发短信还是先打电话?

  1. package JUC.Lock8;
  2. import java.util.concurrent.TimeUnit;
  3. /**
  4. * 8锁,就是关于锁的八个问题
  5. * 6.增加两个静态同步方法,两个个对象,先发短信还是先打电话? 发短信,因为静态的方法从属于类则锁锁的就是类了,而sendSms短信是最先拿到锁的
  6. */
  7. public class Test3 {
  8. public static void main(String[] args) throws InterruptedException {
  9. //两个对象
  10. Phone phone = new Phone();
  11. Phone phone02 = new Phone();
  12. new Thread(() -> {
  13. try {
  14. phone.sendSms();
  15. } catch (InterruptedException e) {
  16. e.printStackTrace();
  17. }
  18. }, "A").start();
  19. TimeUnit.SECONDS.sleep(1);
  20. new Thread(() -> {
  21. phone02.call();
  22. }, "B").start();
  23. }
  24. }
  25. class Phone {
  26. //发短信
  27. public static synchronized void sendSms() throws InterruptedException {
  28. TimeUnit.SECONDS.sleep(4);
  29. System.out.println("发短信中...");
  30. }
  31. //打电话
  32. public static synchronized void call() {
  33. System.out.println("打电话中...");
  34. }
  35. }

问题七:一个静态同步方法,一个非静态同步方法,只有一个对象,先发短信还是先打电话?

  1. package JUC.Lock8;
  2. import java.util.concurrent.TimeUnit;
  3. /**
  4. * 8锁,就是关于锁的八个问题
  5. * 7.一个静态同步方法,一个非静态同步方法,只有一个对象,先发短信还是先打电话? 打电话,因为一个锁的是class一个锁的是对象,但是发短信会延迟4秒而打电话只延迟一秒所以是打电话.
  6. */
  7. public class Test4 {
  8. public static void main(String[] args) throws InterruptedException {
  9. //两个对象
  10. Phone phone = new Phone();
  11. new Thread(() -> {
  12. try {
  13. phone.sendSms();
  14. } catch (InterruptedException e) {
  15. e.printStackTrace();
  16. }
  17. }, "A").start();
  18. TimeUnit.SECONDS.sleep(1);
  19. new Thread(() -> {
  20. phone.call();
  21. }, "B").start();
  22. }
  23. }
  24. class Phone {
  25. //发短信
  26. public static synchronized void sendSms() throws InterruptedException {
  27. TimeUnit.SECONDS.sleep(4);
  28. System.out.println("发短信中...");
  29. }
  30. //打电话
  31. public synchronized void call() {
  32. System.out.println("打电话中...");
  33. }
  34. }

问题八:一个静态同步方法,一个非静态同步方法,两个对象,先发短信还是先打电话?

  1. package JUC.Lock8;
  2. import java.util.concurrent.TimeUnit;
  3. /**
  4. * 8锁,就是关于锁的八个问题
  5. * 8.一个静态同步方法,一个非静态同步方法,两个对象,先发短信还是先打电话? 打电话,因为一个锁的是class一个锁的是对象,但是发短信会延迟4秒而打电话只延迟一秒所以是打电话.
  6. */
  7. public class Test4 {
  8. public static void main(String[] args) throws InterruptedException {
  9. //两个对象
  10. Phone phone = new Phone();
  11. Phone phone02 = new Phone();
  12. new Thread(() -> {
  13. try {
  14. phone.sendSms();
  15. } catch (InterruptedException e) {
  16. e.printStackTrace();
  17. }
  18. }, "A").start();
  19. TimeUnit.SECONDS.sleep(1);
  20. new Thread(() -> {
  21. phone02.call();
  22. }, "B").start();
  23. }
  24. }
  25. class Phone {
  26. //发短信
  27. public static synchronized void sendSms() throws InterruptedException {
  28. TimeUnit.SECONDS.sleep(4);
  29. System.out.println("发短信中...");
  30. }
  31. //打电话
  32. public synchronized void call() {
  33. System.out.println("打电话中...");
  34. }
  35. }

小结:
如果是非静态的synchronized是锁的调用它的对象!
如果为静态的synchronized是锁的是Class类模板!

6.集合安全问题

List

当在单线程中List是安全的,但在并发中ArrayList是不安全的如:

  1. package JUC.List;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. import java.util.UUID;
  5. public class TestList {
  6. public static void main(String[] args) {
  7. List<String> list = new ArrayList<>();
  8. for (int i = 1; i <= 100; i++) {
  9. new Thread(()->{
  10. list.add(UUID.randomUUID().toString().substring(0, 10));
  11. System.out.println(list);
  12. },String.valueOf(i)).start();
  13. }
  14. }
  15. }

就会出现了:ConcurrentModificationException(并发修改异常),解决方案:

  • 使用synchronized给ArrayList的add方法加锁(ArrayList是1.2出来的)
  • 使用List的子类Vector集合类着里面加入了synchronized(Vector是1.0出来的)
  • 使用Collections.synchronizedList()方法将集合转换为安全的集合
  • 使用CopyOnWriteArrayList类在java并发包concurrent下!

这里推荐使用CopyOnWriteArrayList因为:

  • 不同于Vector,它并不是锁的对象,而是锁了进行修改的数组,提高了效率
  • 它的add方法对传入的数据进行了Copy然后才进行保存,提高了安全。 ``` package JUC.List;

import java.util.*; import java.util.concurrent.CopyOnWriteArrayList;

public class TestList { public static void main(String[] args) { List list = new CopyOnWriteArrayList<>(); for (int i = 1; i <= 100; i++) { new Thread(()->{ list.add(UUID.randomUUID().toString().substring(0, 10)); System.out.println(list); },String.valueOf(i)).start(); } } }

  1. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/21379295/1657162484749-6990c680-983e-431e-8131-120a1171b791.png#clientId=u3a72c1e9-ef6d-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=uc7b18095&margin=%5Bobject%20Object%5D&name=image.png&originHeight=265&originWidth=809&originalType=url&ratio=1&rotation=0&showTitle=false&size=22434&status=done&style=none&taskId=u4b9b6eba-d214-4ee6-89e8-96d2524047f&title=)
  2. <a name="Qoao3"></a>
  3. ### Set

package JUC.List;

import java.util.*;

//同理可得:ConcurrentModificationException(并发修改异常) public class TestList { public static void main(String[] args) { Set set = new HashSet<>(); for (int i = 1; i <= 100; i++) { new Thread(()->{ set.add(UUID.randomUUID().toString().substring(0, 10)); System.out.println(set); },String.valueOf(i)).start(); } } }

  1. 解决方案:
  2. - 使用Collections.synchronizedSet()方法将集合转换为安全的set集合!
  3. - 使用ConcurrentSkipListSet类在java并发包concurrent下!
  4. HashSet的底层就是使用了HashMap的键去存储的!同理ConcurrentSkipListSet类的底层是使用了ConcurrentSkipListMap的键进行存储!
  5. <a name="A4Fcl"></a>
  6. ### Map
  7. **面试题:**<br />**Map是这样用的吗?**
  8. > HashMap<Object, Object> objectObjectHashMap = new HashMap<>();
  9. **默认等价什么?**<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/21379295/1657162556521-2f8a2a8f-8254-49dc-a4fd-bd0af4eb86bd.png#clientId=u3a72c1e9-ef6d-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u72ff1eca&margin=%5Bobject%20Object%5D&name=image.png&originHeight=255&originWidth=881&originalType=url&ratio=1&rotation=0&showTitle=false&size=35856&status=done&style=none&taskId=uafdfff15-8785-4e0e-911f-1124806c88b&title=)<br />**默认的容量为16**<br />**默认的加载因子是0.75**<br />**同时HashMap也是不安全的**

package JUC.List;

import java.util.HashMap; import java.util.UUID;

//同理会报:ConcurrentModificationException(并发修改异常) public class TestMap { public static void main(String[] args) { HashMap hashMap = new HashMap<>(); for (int i = 0; i < 100; i++) { new Thread(()->{ hashMap.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 10)); System.out.println(hashMap); }).start(); } } }

  1. **解决方案:**
  2. - 使用Collections.synchronizedSortedMap()把Map转换为安全的集合
  3. - 使用ConcurrentHashMap类在java并发包concurrent下!
  4. <a name="SrGSP"></a>
  5. ## 7.Callable (简单)
  6. <a name="TDEkj"></a>
  7. ### Callable多线程的第三种实现方式:
  8. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/21379295/1657162697817-9c4177a0-d0b5-489d-92d3-674b54f068a3.png#clientId=u3a72c1e9-ef6d-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=uf8db5b24&margin=%5Bobject%20Object%5D&name=image.png&originHeight=122&originWidth=978&originalType=url&ratio=1&rotation=0&showTitle=false&size=16500&status=done&style=none&taskId=u6129336d-3110-4856-b78a-4097b87d5a4&title=)
  9. 1. 表示有返回值
  10. 1. 可以抛出异常
  11. 1. 方法不同,run()/call()
  12. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/21379295/1657162737722-2b38ea14-7b7b-4be8-875f-ca30c17ce238.png#clientId=u3a72c1e9-ef6d-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u855d7150&margin=%5Bobject%20Object%5D&name=image.png&originHeight=693&originWidth=1021&originalType=url&ratio=1&rotation=0&showTitle=false&size=196805&status=done&style=none&taskId=u6a0b8ec3-e77f-4df6-8754-0325bdf85da&title=)** 而这样如何去调用Thread进行启动Callable线程了?**![image.png](https://cdn.nlark.com/yuque/0/2022/png/21379295/1657162737436-184a28d8-77b0-4847-bb5c-07a04e9bee89.png#clientId=u3a72c1e9-ef6d-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u0c85f79c&margin=%5Bobject%20Object%5D&name=image.png&originHeight=252&originWidth=1110&originalType=url&ratio=1&rotation=0&showTitle=false&size=22686&status=done&style=none&taskId=ue27b9af7-92e6-4569-a6fb-99dc065fcb7&title=)<br />这里就有一个实现了Runnable的类叫做[FutureTask](https://blog.csdn.net/abc98526/java/util/concurrent/FutureTask.html)类(适配器模式),它去实现了Runnable接口可以通过它去进行调用Thread。<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/21379295/1657162737323-d7d4981d-2a26-433b-ac07-376f9c62edc8.png#clientId=u3a72c1e9-ef6d-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=uadd76538&margin=%5Bobject%20Object%5D&name=image.png&originHeight=161&originWidth=870&originalType=url&ratio=1&rotation=0&showTitle=false&size=19374&status=done&style=none&taskId=u13c228c8-435f-4770-ad28-2d4fdfa71f6&title=)<br />如:

package JUC.Callable;

import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask;

public class TestCallable { public static void main(String[] args) throws ExecutionException, InterruptedException { //创建适配器 FutureTask futureTask = new FutureTask<>(new MyCallable()); //因为FutureTask实现了RunnableFuture接口所以可以通过Thread启动线程 new Thread(futureTask,”A”).start(); //获取线程返回值 System.out.println(futureTask.get()); } }

class MyCallable implements Callable {

  1. /**
  2. * Computes a result, or throws an exception if unable to do so.
  3. *
  4. * @return computed result
  5. * @throws Exception if unable to compute a result
  6. */
  7. @Override
  8. public Integer call() throws Exception {
  9. System.out.println("你好");
  10. return 1024;
  11. }

}

  1. <br />
  2. ** 细节问题:**
  3. - 有缓存!
  4. - 接口可能会遭到堵塞,需要等待线程执行完毕!
  5. <a name="uRUuN"></a>
  6. ## 8.常用辅助类(必会)
  7. <a name="ktP9p"></a>
  8. ### CountDownLatch类:
  9. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/21379295/1657162802814-6fd07c4e-3a5f-4197-815f-001e5483e083.png#clientId=u3a72c1e9-ef6d-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u2ad41a66&margin=%5Bobject%20Object%5D&name=image.png&originHeight=376&originWidth=863&originalType=url&ratio=1&rotation=0&showTitle=false&size=44840&status=done&style=none&taskId=u9a9e12a8-1ba2-4d26-b82e-05921c280e3&title=)<br />该类可以用作一个减法计数器去执行实例:

package JUC.AuxiliaryClass;

import java.util.concurrent.CountDownLatch;

//计数器 public class TestCountDownLatch { public static void main(String[] args) throws InterruptedException { //总数是6 CountDownLatch countDownLatch = new CountDownLatch(6); for (int i = 0; i < 6; i++) { new Thread(() -> { System.out.println(Thread.currentThread().getName()+”走了”); countDownLatch.countDown();//表示数量-1 },String.valueOf(i)).start(); } //等待计数器归零在往下执行! countDownLatch.await();

  1. System.out.println("关门!");
  2. }

}

  1. **常用方法:**<br /> countDownLatch.countDown(); //表示数量-1<br />countDownLatch.await(); //等待计数器归零在往下执行!<br />原理:当每次有线程调用 countDown()方法则数量减一,假设数量变为0,await()方法就会被唤醒继续执行!
  2. <a name="vEUIC"></a>
  3. ### CyclicBarrier类
  4. **加法计数器:**

package JUC.AuxiliaryClass;

import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit;

public class TestSemaphore { public static void main(String[] args) { //线程数量,停车位,限流的时候会用 Semaphore semaphore = new Semaphore(3); for (int i = 0; i < 8; i++) { new Thread(()->{ //acquire() 得到 //release() 释放 try { //得到车位 semaphore.acquire(); System.out.println(Thread.currentThread().getName()+”得到车位!”); TimeUnit.SECONDS.sleep(2); System.out.println(Thread.currentThread().getName()+”停车两秒后离开车位!”); } catch (InterruptedException e) { e.printStackTrace(); }finally { //离开车位 semaphore.release(); } }).start(); } } }

  1. <a name="VMuss"></a>
  2. ### Semaphore类
  3. **实例:**

package JUC.AuxiliaryClass;

import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit;

public class TestSemaphore { public static void main(String[] args) { //线程数量,停车位,限流的时候会用 Semaphore semaphore = new Semaphore(3); for (int i = 0; i < 8; i++) { new Thread(()->{ //acquire() 得到 //release() 释放 try { //得到车位 semaphore.acquire(); System.out.println(Thread.currentThread().getName()+”得到车位!”); TimeUnit.SECONDS.sleep(2); System.out.println(Thread.currentThread().getName()+”停车两秒后离开车位!”); } catch (InterruptedException e) { e.printStackTrace(); }finally { //离开车位 semaphore.release(); } }).start(); } } }

  1. **常用方法:**
  2. - **semaphore.acquire(); //获得**
  3. - **semaphore.release(); //释放**
  4. 原理:使用**acquire()方法获取一个资源,如果没有则等待,release()释放资源则资源+1。**<br />作用:多个共享资源的互斥使用!并发限流,控制最大线程数!
  5. <a name="mhxEG"></a>
  6. ## 9.读写锁
  7. <a name="Sgnau"></a>
  8. ### ReadWriteLock
  9. `**ReadWriteLock**`**叫做读写锁是**`**java.util.concurrent.locks**`**包下的,它的写锁可以进行一次只被一个线程共享的操作,读锁则可以被所有线程共享,锁已又称为,独占锁(写锁)和共享锁(读锁),如:**

package JUC.Lock;

import java.util.HashMap; import java.util.concurrent.locks.ReentrantReadWriteLock;

/**

  • 小结:
  • 独占锁(写锁):一次只能被一个线程占用
  • 共享锁(读锁):多线程可以同时占有
  • 读-读:可以共存!
  • 读-写:不能共存!
  • 写-写:不能共存! */ public class TestReadWriteLock { public static void main(String[] args) {

    1. MyCacheLock myCache = new MyCacheLock();
    2. //存写的线程
    3. for (int i = 0; i < 5; i++) {
    4. int temp=i;
    5. new Thread(()->{
    6. myCache .put(String.valueOf(temp),temp);
    7. },String.valueOf(i)).start();
    8. }
    9. //读写的线程
    10. for (int i = 0; i < 5; i++) {
    11. int temp=i;
    12. new Thread(()->{
    13. myCache.get(String.valueOf(temp));
    14. },String.valueOf(i)).start();
    15. }

    } }

/**

  • 自定义缓存 */ class MyCache{ private volatile HashMap map=new HashMap<>();

    //存写 public void put(String key,Object value){

    1. System.out.println(Thread.currentThread().getName()+"正在写入"+key+":"+value);
    2. map.put(key,value);
    3. System.out.println(Thread.currentThread().getName()+"写入完毕");

    }

    //读取 public void get(String key){

    1. System.out.println(Thread.currentThread().getName()+"正在读取"+key);
    2. Object o = map.get(key);
    3. System.out.println(Thread.currentThread().getName()+"读取完毕");

    } }

/**

  • 加锁的自定义缓存 */ class MyCacheLock{ private volatile HashMap map=new HashMap<>(); //读写锁,更加细腻度的控制 private final ReentrantReadWriteLock lock=new ReentrantReadWriteLock();

    //存写 (存写的时候只希望有一个线程进行) public void put(String key,Object value){

    1. //上一把写锁
    2. lock.writeLock().lock();
    3. try {
    4. System.out.println(Thread.currentThread().getName()+"正在写入"+key+":"+value);
    5. map.put(key,value);
    6. System.out.println(Thread.currentThread().getName()+"写入完毕");
    7. } finally {
    8. lock.writeLock().unlock();
    9. }

    }

    //读取 (所有人都可以读) public void get(String key){

    1. lock.readLock().lock();
    2. try {
    3. System.out.println(Thread.currentThread().getName()+"正在读取"+key);
    4. Object o = map.get(key);
    5. System.out.println(Thread.currentThread().getName()+"读取完毕");
    6. } finally {
    7. lock.readLock().unlock();
    8. }

    } }

  1. <a name="tA9Mu"></a>
  2. ## 10.阻塞队列
  3. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/21379295/1657163188808-25e90ec1-e3ec-4483-8246-b4f7e0fae167.png#clientId=u3a72c1e9-ef6d-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u3f713c9c&margin=%5Bobject%20Object%5D&name=image.png&originHeight=504&originWidth=989&originalType=url&ratio=1&rotation=0&showTitle=false&size=191104&status=done&style=none&taskId=u22381319-3b4a-4a83-ae9d-fc7b483e1ac&title=)<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/21379295/1657163188720-5a782525-11f6-4b14-b8e6-70b3652fa579.png#clientId=u3a72c1e9-ef6d-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u0436ffef&margin=%5Bobject%20Object%5D&name=image.png&originHeight=319&originWidth=1079&originalType=url&ratio=1&rotation=0&showTitle=false&size=28504&status=done&style=none&taskId=ucbaab505-23c4-459f-83f0-9797e0c802a&title=)<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/21379295/1657163188849-6cab301a-3e1f-4e36-8a92-a71d1ed2367e.png#clientId=u3a72c1e9-ef6d-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u4ee7af35&margin=%5Bobject%20Object%5D&name=image.png&originHeight=321&originWidth=1337&originalType=url&ratio=1&rotation=0&showTitle=false&size=32445&status=done&style=none&taskId=u748c050f-7b53-44a6-824d-1e77b66c52a&title=)<br />BlockingQueue不是新的东西同它的老祖宗也继承了Collection接口(同时也继承了Queue队列接口))<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/21379295/1657163188805-78ef31b7-245c-4af5-ae7f-d504b08df73c.png#clientId=u3a72c1e9-ef6d-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u2e74b889&margin=%5Bobject%20Object%5D&name=image.png&originHeight=379&originWidth=821&originalType=url&ratio=1&rotation=0&showTitle=false&size=19225&status=done&style=none&taskId=u955f47ab-2148-4446-af3b-2d4b2ad80fc&title=)<br />**什么情况下会使用堵塞队列?**
  4. - 多线程并发处理
  5. - 线程池!
  6. <a name="Dx20u"></a>
  7. ### 使用队列
  8. 四组API:
  9. | **方式 ** | **抛出异常** | **有返回值,不抛出异常** | **阻塞 等待** | **超时等待** |
  10. | --- | --- | --- | --- | --- |
  11. | **添加** | **add** | **offer** | **put ** | **offer(等待时间,等待单位)** |
  12. | **移除** | **remove** | **poll** | **take** | **poll(等待时间,等待单位)** |
  13. | **检测队首元素** | **element** | **peek** | **peek** | **peek** |
  1. //会抛出异常
  2. public static void test1(){
  3. ArrayBlockingQueue<Object> objects = new ArrayBlockingQueue<>(3);
  4. System.out.println(objects.add("a"));
  5. System.out.println(objects.add("b"));
  6. System.out.println(objects.add("c"));
  7. //IllegalStateException: Queue full(队列已满异常!)
  8. //System.out.println(objects.add("d"));
  9. System.out.println("===================");
  10. System.out.println(objects.remove());
  11. System.out.println(objects.remove());
  12. System.out.println(objects.remove());
  13. //NoSuchElementException(队列为空异常!)
  14. //System.out.println(objects.remove());
  15. }
  16. //有返回值.不抛出异常
  17. public static void test2(){
  18. ArrayBlockingQueue<Object> objects = new ArrayBlockingQueue<>(3);
  19. System.out.println(objects.offer("a"));
  20. System.out.println(objects.offer("b"));
  21. System.out.println(objects.offer("c"));
  22. System.out.println(objects.offer("d"));//false
  23. System.out.println("===================");
  24. System.out.println(objects.poll());
  25. System.out.println(objects.poll());
  26. System.out.println(objects.poll());
  27. System.out.println(objects.poll());//null
  28. }
  29. //等待,阻塞(一直阻塞)
  30. public static void test3() throws InterruptedException {
  31. ArrayBlockingQueue<Object> arrayBlockingQueue = new ArrayBlockingQueue<>(3);
  32. arrayBlockingQueue.put("a");
  33. arrayBlockingQueue.put("b");
  34. arrayBlockingQueue.put("c");
  35. //arrayBlockingQueue.put("d");//队列没有位置了则会等待
  36. System.out.println(arrayBlockingQueue.take());
  37. System.out.println(arrayBlockingQueue.take());
  38. System.out.println(arrayBlockingQueue.take());
  39. //System.out.println(arrayBlockingQueue.take()); //队列没有元素了则会等待
  40. }
  41. //超时等待
  42. public static void test4() throws InterruptedException {
  43. ArrayBlockingQueue<Object> arrayBlockingQueue = new ArrayBlockingQueue<>(3);
  44. System.out.println(arrayBlockingQueue.offer("a"));
  45. System.out.println(arrayBlockingQueue.offer("b"));
  46. System.out.println(arrayBlockingQueue.offer("c"));
  47. System.out.println(arrayBlockingQueue.offer("d", 2, TimeUnit.SECONDS));//等待超过两秒就退出
  48. System.out.println("===================");
  49. System.out.println(arrayBlockingQueue.poll());
  50. System.out.println(arrayBlockingQueue.poll());
  51. System.out.println(arrayBlockingQueue.poll());
  52. System.out.println(arrayBlockingQueue.poll(2, TimeUnit.SECONDS));//等待超过两秒就退出
  53. }
  1. <a name="TcUqi"></a>
  2. ### SynchronousQueue(同步队列)
  3. 没有容量,当进去一个元素必须等待取出才能再次进入元素!

package JUC.Queue;

import java.util.concurrent.SynchronousQueue; import java.util.concurrent.TimeUnit;

public class TestSynchronousQueue { public static void main(String[] args) throws InterruptedException { SynchronousQueue objects = new SynchronousQueue<>(); new Thread(()->{ try { objects.put(“a”); System.out.println(Thread.currentThread().getName()+”put a”); objects.put(“b”); System.out.println(Thread.currentThread().getName()+”put b”); objects.put(“c”); System.out.println(Thread.currentThread().getName()+”put c”); } catch (InterruptedException e) { e.printStackTrace(); } },”T1”).start();

  1. new Thread(()->{
  2. try {
  3. TimeUnit.SECONDS.sleep(3);
  4. System.out.println(Thread.currentThread().getName()+"->"+objects.take());
  5. TimeUnit.SECONDS.sleep(3);
  6. System.out.println(Thread.currentThread().getName()+"->"+objects.take());
  7. TimeUnit.SECONDS.sleep(3);
  8. System.out.println(Thread.currentThread().getName()+"->"+objects.take());
  9. } catch (InterruptedException e) {
  10. e.printStackTrace();
  11. }
  12. },"T2").start();
  13. }

} ```