线程通信

前言

对于以下两种场景:

1.子线程执行完毕之后,通知主线程处理某些逻辑的场景。

2.线程A执行某个操作之后通知线程B执行某些操作。

这两种场景涉及到线程间通信,可以通过以下几种方式来实现。

1)等待通知机制

两个线程通过对同一对象调用等待 wait() 和通知 notify() 方法来进行通讯。

两个线程交替打印奇数偶数的示例:

 1 public class NumberThreadTest {
 2 
 3     private int start = 1;
 4 
 5     private boolean flag = false;
 6 
 7     public static void main(String[] args) {
 8         NumberThreadTest numberThreadTest = new NumberThreadTest();
 9         OuNum ouNum = new OuNum(numberThreadTest);
10         Thread tdOne = new Thread(ouNum);
11         tdOne.setName("偶数线程A");
12         Thread tdTwo = new Thread(new JiNum(numberThreadTest));
13         tdTwo.setName("奇数线程B");
14         tdOne.start();
15         tdTwo.start();
16     }
17 
18     private static class OuNum implements Runnable {
19 
20         private NumberThreadTest numberThreadTest;
21 
22         public OuNum(NumberThreadTest numberThreadTest) {
23             this.numberThreadTest = numberThreadTest;
24         }
25 
26         @Override
27         public void run() {
28             synchronized (NumberThreadTest.class) {
29                 while (numberThreadTest.start <= 100) {
30 
31                     if (numberThreadTest.flag) {
32                         System.out.println(Thread.currentThread().getName() + "打印值为偶数:" + numberThreadTest.start);
33                         numberThreadTest.start++;
34                         numberThreadTest.flag = false;
35                         NumberThreadTest.class.notify();
36                     } else {
37                         try {
38                             NumberThreadTest.class.wait();
39                         } catch (InterruptedException e) {
40                             e.printStackTrace();
41                         }
42                     }
43                 }
44             }
45         }
46     }
47 
48     private static class JiNum implements Runnable {
49 
50         private NumberThreadTest numberThreadTest;
51 
52         public JiNum(NumberThreadTest numberThreadTest) {
53             this.numberThreadTest = numberThreadTest;
54         }
55 
56         @Override
57         public void run() {
58             synchronized (NumberThreadTest.class) {
59                 while (numberThreadTest.start <= 100) {
60                     if (!numberThreadTest.flag) {
61                         System.out.println(Thread.currentThread().getName() + "打印值为奇数:" + numberThreadTest.start);
62                         numberThreadTest.start++;
63                         numberThreadTest.flag = true;
64                         NumberThreadTest.class.notify();
65                     } else {
66                         try {
67                             NumberThreadTest.class.wait();
68                         } catch (InterruptedException e) {
69                             e.printStackTrace();
70                         }
71                     }
72                 }
73             }
74         }
75     }
76 }

这里的线程 A 和线程 B 都对同一个对象 NumberThreadTest.class 获取锁,A 线程调用了同步对象的 wait() 方法释放了锁并进入 WAITING 状态。

B 线程调用了 notify() 方法,这样 A 线程收到通知之后就可以从 wait() 方法中返回。

这里利用了 NumberThreadTest.class 对象完成了通信。

等待通知有着一个经典范式:

线程 A 作为消费者:

  1. 获取对象的锁。
  2. 进入 while(判断条件),并调用 wait() 方法。
  3. 当条件满足跳出循环执行具体处理逻辑。

线程 B 作为生产者:

  1. 获取对象锁。
  2. 更改与线程 A 共用的判断条件。
  3. 调用 notify() 方法。

volatile 共享内存

CountDownLatch 并发工具 

CountDownLatch使一个线程(主线程)等待其他线程(多个子线程)各自执行完毕后再执行该主线程。

通过一个计数器来实现的,计数器的初始值一般设定为子线程的数量。每当一个线程(子线程)执行完毕后,计数器的值就-1,当计数器的值为0时,表示所有子线程都执行完毕,然后在闭锁上等待的线程就可以恢复工作了(主线程就可以继续执行了)。

 1 public class CountDownTest {
 2     private static final CountDownLatch countDownLatch = new CountDownLatch(2);
 3 
 4     private static volatile int count = 0;
 5 
 6     public static void main(String[] args) throws InterruptedException {
 7         PrintJiNum printJiNum = new PrintJiNum();
 8         PrintOuNum printOuNum = new PrintOuNum();
 9         System.out.println("-------开始执行--------");
10         new Thread(printJiNum).start();
11         new Thread(printOuNum).start();
12         countDownLatch.await();
13         System.out.println("线程执行完成");
14     }
15 
16     private static class PrintJiNum implements Runnable {
17         @Override
18         public void run() {
19             System.out.println("打印奇数线程现在开始执行------");
20             while (count <= 100) {
21                 if (count % 2 == 0) {
22                     System.out.println(Thread.currentThread().getName() + "------" + count);
23                     count++;
24                 }
25             }
26             countDownLatch.countDown();
27         }
28     }
29 
30     private static class PrintOuNum implements Runnable {
31 
32         @Override
33         public void run() {
34             System.out.println("打印偶数线程现在开始执行------");
35             while (count <= 100) {
36                 if (count % 2 != 0) {
37                     System.out.println(Thread.currentThread().getName() + "------" + count);
38                     count++;
39                 }
40             }
41             countDownLatch.countDown();
42         }
43     }
44 }

CyclicBarrier 并发工具

CyclicBarrier 中文名叫做屏障或者是栅栏,也可以用于线程间通信。

它可以等待 N 个线程都达到某个状态后然后多个线程才可以继续运行。

  1. 首先初始化线程参与者。
  2. 调用 await() 将会在所有参与者线程都调用之前等待。
  3. 直到所有参与者都调用了 await() 后,所有线程从 await() 返回继续后续逻辑。
 1 private static void cyclicBarrier() throws Exception {
 2         CyclicBarrier cyclicBarrier = new CyclicBarrier(3) ;
 3 
 4         new Thread(new Runnable() {
 5             @Override
 6             public void run() {
 7                 LOGGER.info("thread run");
 8                 try {
 9                     cyclicBarrier.await() ;
10                 } catch (Exception e) {
11                     e.printStackTrace();
12                 }
13 
14                 LOGGER.info("thread end do something");
15             }
16         }).start();
17 
18         new Thread(new Runnable() {
19             @Override
20             public void run() {
21                 LOGGER.info("thread run");
22                 try {
23                     cyclicBarrier.await() ;
24                 } catch (Exception e) {
25                     e.printStackTrace();
26                 }
27 
28                 LOGGER.info("thread end do something");
29             }
30         }).start();
31 
32         new Thread(new Runnable() {
33             @Override
34             public void run() {
35                 LOGGER.info("thread run");
36                 try {
37                     Thread.sleep(5000);
38                     cyclicBarrier.await() ;
39                 } catch (Exception e) {
40                     e.printStackTrace();
41                 }
42 
43                 LOGGER.info("thread end do something");
44             }
45         }).start();
46 
47         LOGGER.info("main thread");
48     }

Semaphore并发工具

Semaphore也是一个线程同步的辅助类,可以维护当前访问自身的线程个数,并提供了同步机制。使用Semaphore可以控制同时访问资源的线程个数,例如,实现一个文件允许的并发访问数。

Semaphore的主要方法摘要:

  void acquire():从此信号量获取一个许可,在提供一个许可前一直将线程阻塞,否则线程被中断。

  void release():释放一个许可,将其返回给信号量。

  int availablePermits():返回此信号量中当前可用的许可数。

  boolean hasQueuedThreads():查询是否有线程正在等待获取。

 1 public class SemaphoreMutex {
 2     //初始化为1,互斥信号量
 3     private final static Semaphore mutex = new Semaphore(1);
 4 
 5     public static void main(String[] args) {
 6         ExecutorService pools = Executors.newCachedThreadPool();
 7 
 8         for (int i = 0; i < 10; i++) {
 9             final int index = i;
10             Runnable run = new Runnable() {
11                 @Override
12                 public void run() {
13                     try {
14                         mutex.acquire();
15                         System.out.println(String.format("[Thread-%s]任务id --- %s--%s",
16                                 Thread.currentThread().getId(), index, LocalDateTime.now()));
17                         TimeUnit.SECONDS.sleep(2);
18 
19                     } catch (InterruptedException e) {
20                         e.printStackTrace();
21                     } finally {
22                         //使用完成释放锁
23                         mutex.release();
24                         System.out.println("锁释放");
25                     }
26                 }
27             };
28             pools.execute(run);
29         }
30         pools.shutdown();
31     }
32 }

 

posted @ 2020-04-16 16:08  seedss  阅读(217)  评论(0编辑  收藏  举报