线程间通信模型
线程间通信的模型有两种:共享内存和消息传递,以下方式都是基本这两种模型来实现的。
我们来基本一道面试常见的题目来分析
场景:两个线程,一个线程对当前数值加 1,另一个线程对当前数值减 1,要求用线程间通信
volatile 方案
基于volatile
关键字来实现线程间相互通信是使用共享内存的思想
大致意思就是多个线程同时监听一个变量,当这个变量发生变化的时候 ,线程能够感知并执行相应的业务。这也是最简单的一种实现方式
首先定义一个实现类DemoClass
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| public class DemoClass {
private int number = 0;
private static volatile boolean flag = true;
private int loop = 0;
public void increment() { try { while (true) { if (flag) { number++; System.out.println("------当前第" + loop + "次循环-----"); System.out.println(Thread.currentThread().getName() + "执行了加一,值为:" + number); flag = false; Thread.sleep(2000); } } } catch (Exception e) { e.printStackTrace(); } }
public void decrement() { try { while (true) { if (!flag) { number--; System.out.println(Thread.currentThread().getName() + "执行了减一,值为:" + number); loop++; flag = true; Thread.sleep(2000); } } } catch (Exception e) { e.printStackTrace(); } } }
|
测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class TestVolatile {
public static void main(String[] args) {
DemoClass demoClass = new DemoClass();
new Thread(() -> { demoClass.increment(); }, "线程一").start();
new Thread(() -> { demoClass.decrement(); }, "线程二").start(); } }
|
synchronized 方案
- 等待池:假设一个线程 A 调用了某个对象的 wait()方法,线程 A 就会释放该对象的锁后,进入到了该对象的等待池,等待池中的线程不会去竞争该对象的锁。
- 锁池:只有获取了对象的锁,线程才能执行对象的
synchronized
代码,对象的锁每次只有一个线程可以获得,其他线程只能在锁池中等待 - 区别:
notify()
方法随机唤醒对象的等待池中的一个线程,进入锁池;notifyAll()
唤醒对象的等待池中的所有线程,进入锁池。 - 关键字
synchronized
与 wait()
/ notify()
这两个方法一起使用用来实现 等待/通知模式
实现类DemoClass
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| public class DemoClass {
private int number = 0;
public synchronized void increment() { try { while (number != 0) { this.wait(); } number++; System.out.println("--------" + Thread.currentThread().getName() + "加一成功----------,值为:" + number); Thread.sleep(2000); this.notifyAll(); } catch (Exception e) { e.printStackTrace(); } }
public synchronized void decrement() { try { while (number == 0) { this.wait(); } number--; System.out.println("--------" + Thread.currentThread().getName() + "减一成功----------,值为:" + number); Thread.sleep(2000); this.notifyAll(); } catch (Exception e) { e.printStackTrace(); } } }
|
测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class TestSynchronized {
public static void main(String[] args) {
DemoClass demoClass = new DemoClass(); new Thread(() -> { for (int i = 0; i < 5; i++) { demoClass.increment(); } }, "线程一").start();
new Thread(() -> { for (int i = 0; i < 5; i++) { demoClass.decrement(); } }, "线程二").start(); } }
|
Lock 方案
Condition
类与 await()
和 signal()/signalAll()
方法一起使用也可以实现等待通知模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| public class DemoClass {
private int number = 0;
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void increment() { try { lock.lock(); while (number != 0) { condition.await(); } number++; System.out.println("--------" + Thread.currentThread().getName() + "加一成功----------,值为:" + number); condition.signalAll(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } }
public void decrement() { try { lock.lock(); while (number == 0) { condition.await(); } number--; System.out.println("--------" + Thread.currentThread().getName() + "减一成功----------,值为:" + number); condition.signalAll(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } }
|
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class TestLock {
public static void main(String[] args) {
com.atguigu.demo7.DemoClass demoClass = new DemoClass(); new Thread(() -> { for (int i = 0; i < 5; i++) { demoClass.increment(); } }, "线程一").start();
new Thread(() -> { for (int i = 0; i < 5; i++) { demoClass.decrement(); } }, "线程二").start(); } }
|
线程间定制化通信
案例介绍
实现 A 线程打印 5 次 A,B 线程打印 10 次 B,C 线程打印 15 次 C,按照此顺序循环 10 轮
实现流程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
| public class DemoClass {
private int number = 0;
private Lock lock = new ReentrantLock();
private Condition conditionA = lock.newCondition();
private Condition conditionB = lock.newCondition();
private Condition conditionC = lock.newCondition();
public void printA(int j) { try { lock.lock(); while (number != 0) { conditionA.await(); } System.out.println("第" + j + "轮开始"); System.out.println(Thread.currentThread().getName() + "输出A"); for (int i = 0; i < 5; i++) { System.out.print("A"); } System.out.println(); number = 1; conditionB.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } }
public void printB() { try { lock.lock(); while (number != 1) { conditionB.await(); } System.out.println(Thread.currentThread().getName() + "输出B"); for (int i = 0; i < 10; i++) { System.out.print("B"); } System.out.println(); number = 2; conditionC.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } }
public void printC() { try { lock.lock(); while (number != 2) { conditionC.await(); } System.out.println(Thread.currentThread().getName() + "输出C"); for (int i = 0; i < 15; i++) { System.out.print("C"); } System.out.println(); System.out.println("-----------------------------------------"); number = 0; conditionA.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } }
|
测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| public class Test {
public static void main(String[] args) {
DemoClass demoClass = new DemoClass();
new Thread(() -> { for (int i = 1; i <= 10; i++) { demoClass.printA(i); } }, "A线程").start();
new Thread(() -> { for (int i = 1; i <= 10; i++) { demoClass.printB(); } }, "B线程").start();
new Thread(() -> { for (int i = 1; i <= 10; i++) { demoClass.printC(); } }, "C线程").start(); } }
|