实现java线程通信的几种方式 讲解java多线程共享数据( 二 )

输出结果:
threadA正在运行 。。。threadA正在运行 。。。threadA正在运行 。。。threadA正在运行 。。。threadA执行完毕这里的 flag 存放于主内存中 。所以主线程和线程 A 都可以看到 。
flag 采用 volatile 修饰主要是为了内存可见性 。更多内容可以查看这里 。
CountDownLatch 并发工具
CountDownLatch 可以实现 join 相同的功能 。但是更加的灵活 。
privatestaticvoidcountDownLatch()throwsException{intthread=3;longstart=System.currentTimeMillis();finalCountDownLatchcountDown=newCountDownLatch(thread);for(inti=0;i<thread;i++){newThread(newRunnable(){@Overridepublicvoidrun(){LOGGER.info("threadrun");try{Thread.sleep(2000);countDown.countDown();LOGGER.info("threadend");}catch(InterruptedExceptione){e.printStackTrace();}}}).start();}countDown.await();longstop=System.currentTimeMillis();LOGGER.info("mainovertotaltime={}",stop-start);}输出结果:
2018-03-1620:19:44.126[Thread-0]INFOc.c.actual.ThreadCommunication-threadrun2018-03-1620:19:44.126[Thread-2]INFOc.c.actual.ThreadCommunication-threadrun2018-03-1620:19:44.126[Thread-1]INFOc.c.actual.ThreadCommunication-threadrun2018-03-1620:19:46.136[Thread-2]INFOc.c.actual.ThreadCommunication-threadend2018-03-1620:19:46.136[Thread-1]INFOc.c.actual.ThreadCommunication-threadend2018-03-1620:19:46.136[Thread-0]INFOc.c.actual.ThreadCommunication-threadend2018-03-1620:19:46.136[main]INFOc.c.actual.ThreadCommunication-mainovertotaltime=2012CountDownLatch 也是基于 AQS(AbstractQueuedSynchronizer) 实现的 。更多实现参考 ReentrantLock 实现原理
初始化一个 CountDownLatch 时告诉并发的线程 。然后在每个线程处理完毕之后调用 countDown() 方法 。
【实现java线程通信的几种方式 讲解java多线程共享数据】该方法会将 AQS 内置的一个 state 状态 -1。
最终在主线程调用 await() 方法 。它会阻塞直到 state == 0 的时候返回 。
CyclicBarrier 并发工具
privatestaticvoidcyclicBarrier()throwsException{CyclicBarriercyclicBarrier=newCyclicBarrier(3);newThread(newRunnable(){@Overridepublicvoidrun(){LOGGER.info("threadrun");try{cyclicBarrier.await();}catch(Exceptione){e.printStackTrace();}LOGGER.info("threadenddosomething");}}).start();newThread(newRunnable(){@Overridepublicvoidrun(){LOGGER.info("threadrun");try{cyclicBarrier.await();}catch(Exceptione){e.printStackTrace();}LOGGER.info("threadenddosomething");}}).start();newThread(newRunnable(){@Overridepublicvoidrun(){LOGGER.info("threadrun");try{Thread.sleep(5000);cyclicBarrier.await();}catch(Exceptione){e.printStackTrace();}LOGGER.info("threadenddosomething");}}).start();LOGGER.info("mainthread");}CyclicBarrier 中文名叫做屏障或者是栅栏 。也可以用于线程间通信 。
它可以等待 N 个线程都达到某个状态后继续运行的效果 。
首先初始化线程参与者 。
调用 await() 将会在所有参与者线程都调用之前等待 。
直到所有参与者都调用了 await() 后 。所有线程从 await() 返回继续后续逻辑 。
运行结果:
2018-03-1822:40:00.731[Thread-0]INFOc.c.actual.ThreadCommunication-threadrun2018-03-1822:40:00.731[Thread-1]INFOc.c.actual.ThreadCommunication-threadrun2018-03-1822:40:00.731[Thread-2]INFOc.c.actual.ThreadCommunication-threadrun2018-03-1822:40:00.731[main]INFOc.c.actual.ThreadCommunication-mainthread2018-03-1822:40:05.741[Thread-0]INFOc.c.actual.ThreadCommunication-threadenddosomething2018-03-1822:40:05.741[Thread-1]INFOc.c.actual.ThreadCommunication-threadenddosomething2018-03-1822:40:05.741[Thread-2]INFOc.c.actual.ThreadCommunication-threadenddosomething可以看出由于其中一个线程休眠了五秒 。所有其余所有的线程都得等待这个线程调用 await()。
该工具可以实现 CountDownLatch 同样的功能 。但是要更加灵活 。甚至可以调用 reset() 方法重置 CyclicBarrier (需要自行捕获 BrokenBarrierException 处理) 然后重新执行 。
线程响应中断
publicclassStopThreadimplementsRunnable{@Overridepublicvoidrun(){while(!Thread.currentThread().isInterrupted()){//线程执行具体逻辑System.out.println(Thread.currentThread().getName()+"运行中 。。");}System.out.println(Thread.currentThread().getName()+"退出 。。");}publicstaticvoidmain(String[]args)throwsInterruptedException{Threadthread=newThread(newStopThread(),"threadA");thread.start();System.out.println("main线程正在运行");TimeUnit.MILLISECONDS.sleep(10);thread.interrupt();}}输出结果:
threadA运行中 。。threadA运行中 。。threadA退出 。。可以采用中断线程的方式来通信 。调用了 thread.interrupt() 方法其实就是将 thread 中的一个标志属性置为了 true 。
并不是说调用了该方法就可以中断线程 。如果不对这个标志进行响应其实是没有什么作用(这里对这个标志进行了判断) 。

推荐阅读