java线程通讯的几种方式

网友投稿 928 2022-05-30

并发编程中,我们可能会遇到这样一个场景

A、B两个线程并行,但是我希望保证B线程在A线程执行完了后再执行

这个时候就需要线程间进行通讯

A执行完了后对B说一声,喂,我执行完了

来康康用Java怎么实现

1、基于synchronized

2、基于reentrantLock

3、基于volatile

4、基于countDownLatch

我目前就知道这四种

1、synchronized+wait() 和 notify()

wait() 和 notify()都是Object类的通讯方法,注意一点,wait和 notify必须搭配synchronized使用,并且notify()不会释放锁

public class SynchronizedTest { //定义个year,用来记录某明星的练习年数 private static double year; public void run() { //线程A,练习唱跳rap Thread threadA = new Thread(() -> { synchronized (this) { for (year = 0.5; year <= 5; year += 0.5) { System.out.println("蔡徐鸡开始练习唱跳rap:已练习" + year + "年"); try { Thread.sleep(288); } catch (InterruptedException e) { e.printStackTrace(); } //众所周知,练习两年半即可出道 if (year == 2.5) { System.out.println("===========================>成功练习两年半,出道!!!"); this.notify(); } } } }); //线程B,练习打篮球 Thread threadB = new Thread(() -> { while (true) { synchronized (this) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("蔡徐鸡开始练习打篮球"); } } }); //注意,一定要先启动B,不然会导致B永远拿不到锁 threadB.start(); threadA.start(); } public static void main(String[] args) { SynchronizedTest test = new SynchronizedTest(); test.run(); } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

java线程间通讯的几种方式

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

运行结果:

蔡徐鸡开始练习唱跳rap:已练习0.5年

蔡徐鸡开始练习唱跳rap:已练习1.0年

蔡徐鸡开始练习唱跳rap:已练习1.5年

蔡徐鸡开始练习唱跳rap:已练习2.0年

蔡徐鸡开始练习唱跳rap:已练习2.5年

===========================>成功练习两年半,出道!!!

蔡徐鸡开始练习唱跳rap:已练习3.0年

蔡徐鸡开始练习唱跳rap:已练习3.5年

蔡徐鸡开始练习唱跳rap:已练习4.0年

蔡徐鸡开始练习唱跳rap:已练习4.5年

蔡徐鸡开始练习唱跳rap:已练习5.0年

蔡徐鸡开始练习打篮球

注意看运行结果,线程A在执行notify后并没有释放锁,而是执行完当前任务才开始执行线程B的任务

2、基于ReentrantLock

ReentrantLock也能实现线程间通讯,不过有点麻烦,需要结合ReentrantLock的Condition

public class LockTest { //定义个year,用来记录某明星练习打篮球的年数 private static double year; public static void main(String[] args) { ReentrantLock lock = new ReentrantLock(); Condition condition = lock.newCondition(); //线程A,练习唱跳rap Thread threadA = new Thread(() -> { //执行业务代码前上锁 lock.lock(); for (year = 0.5; year <= 5; year += 0.5) { System.out.println("蔡徐鸡开始练习唱跳rap:已练习" + year + "年"); try { Thread.sleep(288); } catch (InterruptedException e) { e.printStackTrace(); } //众所周知,练习两年半即可出道 if (year == 2.5) { System.out.println("===========================>成功练习两年半,出道!!!"); //唤醒等待中的线程 condition.signal(); } } //业务代码执行完后解锁 lock.unlock(); }); //线程B,练习打篮球 Thread threadB = new Thread(() -> { //执行业务代码前上锁 lock.lock(); try { //让线程等待,如果计数器为0的话,则立即执行 condition.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("蔡徐老母鸡开始练习打篮球"); //业务代码执行完后解锁 lock.unlock(); }); //注意,一定要先启动B,不然会导致B永远拿不到锁 threadB.start(); threadA.start(); } }

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

运行结果:

蔡徐鸡开始练习唱跳rap:已练习0.5年

蔡徐鸡开始练习唱跳rap:已练习1.0年

蔡徐鸡开始练习唱跳rap:已练习1.5年

蔡徐鸡开始练习唱跳rap:已练习2.0年

蔡徐鸡开始练习唱跳rap:已练习2.5年

===========================>成功练习两年半,出道!!!

蔡徐鸡开始练习唱跳rap:已练习3.0年

蔡徐鸡开始练习唱跳rap:已练习3.5年

蔡徐鸡开始练习唱跳rap:已练习4.0年

蔡徐鸡开始练习唱跳rap:已练习4.5年

蔡徐鸡开始练习唱跳rap:已练习5.0年

蔡徐老母鸡开始练习打篮球

效果和synchronized+wait() 和 notify()一样一样的

3、基于volatile

使用共享变量也能实现,用volatile即可,原理就是多个线程共同监听同个变量,根据变量的值变化来执行对应的任务,此处volatile的作用就是让其它线程能即时感知变量值的改变

public class volatileTest { //定义一个共享变量,注意,必须用volatile修饰 static volatile boolean flag = false; //定义个year,用来记录某明星练习打篮球的年数 private static double year; public static void main(String[] args) { //线程A,练习唱跳rap Thread threadA = new Thread(() -> { while (true) { if (!flag) { for (year = 0.5; year <= 5; year += 0.5) { System.out.println("蔡徐鸡开始练习唱跳rap:已练习" + year + "年"); try { Thread.sleep(288); } catch (InterruptedException e) { e.printStackTrace(); } //众所周知,练习两年半即可出道 if (year == 2.5) { System.out.println("===========================>成功练习两年半,出道!!!"); year = 0.5; flag = true; break; } } } } }); //线程B,练习打篮球 Thread threadB = new Thread(() -> { while (true) { if (flag) { System.out.println("蔡徐老母鸡开始练习打篮球"); break; } } }); // 启动线程 threadA.start(); threadB.start(); } }

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

运行结果:

蔡徐鸡开始练习唱跳rap:已练习0.5年

蔡徐鸡开始练习唱跳rap:已练习1.0年

蔡徐鸡开始练习唱跳rap:已练习1.5年

蔡徐鸡开始练习唱跳rap:已练习2.0年

蔡徐鸡开始练习唱跳rap:已练习2.5年

===========================>成功练习两年半,出道!!!

蔡徐老母鸡开始练习打篮球

3、基于CountDownLatch

CountDownLatch是JUC包下的一个并发编程工具,主要有两个方法,countDown和await,CountDownLatch底层维护了一个计数器,在实例化的时候设置,当调用countDown方法时,计数器减一,如果计数器在减一前已经为0,那么什么都不会发生,如果减一后变成0,则唤醒所有等待的线程;await方法会使当前线程等待,直到计数器为0

public class CountDownLatchTest { //定义个year,用来记录某明星练习打篮球的年数 private static double year; public static void main(String[] args) { CountDownLatch latch = new CountDownLatch(1); //线程A,练习唱跳rap Thread threadA = new Thread(() -> { for (year = 0.5; year <= 5; year += 0.5) { System.out.println("蔡徐鸡开始练习唱跳rap:已练习" + year + "年"); try { Thread.sleep(288); } catch (InterruptedException e) { e.printStackTrace(); } //众所周知,练习两年半即可出道 if (year == 2.5) { System.out.println("===========================>成功练习两年半,出道!!!"); //计数器减一 latch.countDown(); } } }); //线程B,练习打篮球 Thread threadB = new Thread(() -> { try { //让线程等待,如果计数器为0的话,则立即执行 latch.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("蔡徐老母鸡开始练习打篮球"); }); // 启动线程 threadA.start(); threadB.start(); } }

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

运行结果:

蔡徐鸡开始练习唱跳rap:已练习0.5年

蔡徐鸡开始练习唱跳rap:已练习1.0年

蔡徐鸡开始练习唱跳rap:已练习1.5年

蔡徐鸡开始练习唱跳rap:已练习2.0年

蔡徐鸡开始练习唱跳rap:已练习2.5年

===========================>成功练习两年半,出道!!!

蔡徐鸡开始练习唱跳rap:已练习3.0年

蔡徐老母鸡开始练习打篮球

蔡徐鸡开始练习唱跳rap:已练习3.5年

蔡徐鸡开始练习唱跳rap:已练习4.0年

蔡徐鸡开始练习唱跳rap:已练习4.5年

蔡徐鸡开始练习唱跳rap:已练习5.0年

如果你多运行几次,你会发现线程B执行的时机是随机的,但永远在计数器为0后才开始执行,也就是说计数器为0后,线程A和线程B谁抢到锁就谁执行

文中所有demo都是复制即可运行,大家还是要多动手,家里有条件的都用idea跑一跑,没条件的可以用手抄

嘤~

ok我话说完

Java JDK 任务调度

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:在 Linux 上设置打印机的 12 个 CUPS lpadmin 命令示例
下一篇:学好并发编程,关键是要理解这三个核心问题!
相关文章