Java的几个同步辅助类
2016-03-27 21:32
309 查看
Java为我们提供了一些
再CountDownLatch中两个比较关键的方法:
设想有这样一个功能需要Thread1、Thread2、Thread3、Thread4四条线程分别统计C、D、E、F四个盘的大小,所有线程都统计完毕交给主线程去做汇总,利用CountDownLatch来完成就非常轻松。
在
在所有的屏障都到达之后,会启动一个线程来运行这里面的代码。这里举一个例子:百米赛跑的运动员起跑前需要准备,所有选手准备完毕之后,才可以同时起跑。
输出:
相比
运行的结果就是:
虽然线程池允许6个最大线程数量,但是同一个时间内只用三个任务被执行。
同步辅助类,利用这些辅助类我们可以在多线程编程中,灵活地把握线程的状态。
CountDownLatch
CountDownLatch一个
同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
再CountDownLatch中两个比较关键的方法:
public void await() throws InterruptedException; public void countDown();
CountDownLatch是一个
计数器,它的构造方法中需要设置一个数值,用来设定计数的次数。每次调用
countDown()方法之后,这个计数器都会减去
1,CountDownLatch会一直阻塞着调用
await()方法的线程,直到
计数器的值变为
0。
设想有这样一个功能需要Thread1、Thread2、Thread3、Thread4四条线程分别统计C、D、E、F四个盘的大小,所有线程都统计完毕交给主线程去做汇总,利用CountDownLatch来完成就非常轻松。
public class CountDownLatchTest { private static CountDownLatch count = new CountDownLatch(4); private static ExecutorService service = Executors.newFixedThreadPool(6); public static void main(String args[]) throws InterruptedException { for (int i = 0; i < 4; i++) { service.execute(() -> { // 模拟任务耗时 try { int timer = new Random().nextInt(5); TimeUnit.SECONDS.sleep(timer); System.out.printf("%s时完成磁盘的统计任务,耗费%d秒.\n", new Date().toString(), timer); // 任务完成之后,计数器减一 count.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } }); } // 主线程一直被阻塞,知道count的计数器被设置为0 count.await(); System.out.printf("%s时全部任务都完成,执行合并计算.\n", new Date().toString()); service.shutdown(); } }
CyclicBarrier
Barrier在英语中是屏障的意思,这个同步工具会阻塞调用的线程,直到条件满足时,阻塞的线程同时被打开。
public int await() throws InterruptedException, BrokenBarrierException
CyclicBarrier初始化的时候,设置一个屏障数。线程调用
await()方法的时候,这个线程就会被阻塞,当调用
await()的线程数量到达屏障数的时候,主线程就会取消所有被阻塞线程的状态。
在
CyclicBarrier的构造方法中,还可以设置一个
barrierAction。
在所有的屏障都到达之后,会启动一个线程来运行这里面的代码。这里举一个例子:百米赛跑的运动员起跑前需要准备,所有选手准备完毕之后,才可以同时起跑。
public class CyclicBarrierTest { private static CyclicBarrier cyclicBarrier = new CyclicBarrier(8); private static ExecutorService service = Executors.newFixedThreadPool(50); public static void main(String args[]) { for (int i = 1; i < 9; i++) { service.execute(new Thread(new Runner(i, cyclicBarrier))); } service.shutdown(); } } // 运动员类 public class Runner implements Runnable { private int number; private CyclicBarrier cyclicBarrier; public Runner(int number, CyclicBarrier cyclicBarrier) { this.number = number; this.cyclicBarrier = cyclicBarrier; } @Override public void run() { try { int timer = new Random().nextInt(5); TimeUnit.SECONDS.sleep(timer); System.out.printf("%d号选手准备完毕,准备时间%d\n", number, timer); cyclicBarrier.await(); System.out.printf("%d号选手于%s时起跑!\n", number, new Date().toString()); } catch (InterruptedException | BrokenBarrierException e) { e.printStackTrace(); } } }
输出:
1号选手准备完毕,准备时间0 4号选手准备完毕,准备时间0 5号选手准备完毕,准备时间1 8号选手准备完毕,准备时间1 3号选手准备完毕,准备时间2 2号选手准备完毕,准备时间3 7号选手准备完毕,准备时间3 6号选手准备完毕,准备时间3 7号选手于Sun Mar 27 21:19:00 CST 2016时起跑! 2号选手于Sun Mar 27 21:19:00 CST 2016时起跑! 5号选手于Sun Mar 27 21:19:00 CST 2016时起跑! 6号选手于Sun Mar 27 21:19:00 CST 2016时起跑! 3号选手于Sun Mar 27 21:19:00 CST 2016时起跑! 8号选手于Sun Mar 27 21:19:00 CST 2016时起跑! 4号选手于Sun Mar 27 21:19:00 CST 2016时起跑! 1号选手于Sun Mar 27 21:19:00 CST 2016时起跑!
相比
CountDownLatch,
CyclicBarrier是可以被循环使用的,而且遇到线程中断等情况时,还可以利用
reset()方法,重置计数器,从这些方面来说,
CyclicBarrier会比
CountDownLatch更加灵活一些。
Semaphore
Semaphore被用于控制特定资源在同一个时间被访问的个数。类似连接池的概念,保证资源可以被合理的使用。
Semaphore的几个重要方法:
// 获取资源 public void acquire() throws InterruptedException // 释放资源 public void release()
Semaphore的构造方法可以设置一个
int值来设置一个计数器,用于表示资源同时可以被多少外部环境使用。每使用一次
acquire(),计数器都会去减去一,而每次调用
release()计数器则会增加一。当计数器的值为0的时候,外部的环境被阻塞,直到
Semaphore有空闲的资源可以被使用。
public class SemaphoreTest { private static Semaphore semaphore = new Semaphore(3); private static ExecutorService service = Executors.newFixedThreadPool(6); public static void main(String args[]) { // 执行9个任务 for (int i = 0; i < 9; i++) { service.execute(() -> { try { semaphore.acquire(); System.out.printf("%s时获取资源,并调用.\n", new Date().toString()); // 线程挂起3秒 TimeUnit.SECONDS.sleep(3); semaphore.release(); } catch (InterruptedException e) { e.printStackTrace(); } }); } service.shutdown(); } }
运行的结果就是:
Sun Mar 27 20:18:16 CST 2016时获取资源,并调用. Sun Mar 27 20:18:16 CST 2016时获取资源,并调用. Sun Mar 27 20:18:16 CST 2016时获取资源,并调用. Sun Mar 27 20:18:19 CST 2016时获取资源,并调用. Sun Mar 27 20:18:19 CST 2016时获取资源,并调用. Sun Mar 27 20:18:19 CST 2016时获取资源,并调用. Sun Mar 27 20:18:22 CST 2016时获取资源,并调用. Sun Mar 27 20:18:22 CST 2016时获取资源,并调用. Sun Mar 27 20:18:22 CST 2016时获取资源,并调用.
虽然线程池允许6个最大线程数量,但是同一个时间内只用三个任务被执行。
相关文章推荐
- Java基础--反射机制的知识点梳理
- Java笔记---部署 JavaWeb 项目到云服务器
- java 求解第n个丑数
- Java如何获取所查询的结果集的列数,并将每条记录打印出来
- JAVA常用集合框架用法详解基础篇一之Colletion接口
- 排序
- 理解JNDI中 java:comp/env/jdbc/datasource 与 jdbc/datasource 的不同之处。
- Spring动态连接Mongo(mongo读写分离,连接不同副本集)
- Java中finalize方法用途何在?
- Java反射机制探究
- 随堂笔记160322MyEclipse
- Java内存结构
- java基础
- Spring学习——(二)IOC、DI以及常用xml配置
- java设计模式之——代理模式
- Struts2自定义拦截器
- 共同学习Spring源码--ContextLoaderListener和ContextCleanupListener
- MyEclipse------缓冲流的使用
- Android中级学习笔记(三)之eclipse建完aidl文件后gen中没有生成相应的Java文件
- 理解Java虚拟机体系结构