java中并发常用工具类
2017-02-07 20:47
295 查看
前言:在你无聊的时候,想想比你优秀还努力的人,也许就不觉的无聊了
今天下午没事干把买的java并发编程艺术这本书拿出来看了看,看了下也记不住,还是好记性不如烂笔头,今天讲四个并发中可能会用到的工具类,分别是:
CountDownLatch
CyclicBarrier
Semaphore
Exchanger
CountDownLatch
countDownLatch允许一个或多个线程等待其他线程完成操作.比如说有三个线程分别是老二,老大,老爸,这三个线程必须是老二吃好了,老大吃,老大吃完了,老爸吃,在20年钱,农村家里穷,一般有好吃的都是先留个最小的,然后才给其他兄弟姐妹吃,都不吃了,才由我们的父母吃,所以父母都不容易了,为了儿女,虽然是多个线程但是确实线性的,
我同事面试别人就问过好几次这个问题,在java或者android中常用的有2个方式实现
第一种方式:
使用jdk中Thread自带的函数join实现,join()用于当前执行线程等待join线程执行结束,其实实现原理是不停检查join线程是否存活,如果join线程存活则让当前线程永远等待,其中,wait(0)表示永远等待下去,join在jdk中的是实现方式如下:
CountDownLatch是一个同步计数器,现在有个需求,在古代是男人先吃饭,特别是有客人在的时候,女人不能上桌,等待男人吃完,女人才能上桌吃饭,
你会发现CountDownLatch和join还是有区别:
相同点:都能等待一个或者多个线程执行完成操作,比如等待三个线程执行完毕后,第四个线程才能执行
不同点:join能让线程按我们预想的的顺序执行,比如线程1执行完了,线程2才能执行,线程2执行完,线程3才能执行,但是CountDownLatch就做不到.
如果查看CountDownLatch源码,发现这源码还是很少的,没几行代码,方法也就几个:CountDownLatch的构造函数接受一个int类型的参数作为计时器,如果你想等待N个点完成,就在这里传入N
当我们调用CountDownLatch的countDown方法时,N就会减1,CountDownLatch的await方法会阻塞当前线程,直到N变为零(也就是线程都执行完了),由于countDown方法可以用在任何地方,所以这里说的N个点,可以是N个线程,也可以是1个线程里的N个执行步骤。用在多线程时,只需把这个CountDownLatch的引用传递到线程中即可,
当然阻塞还有一个重载的方法就是await(long timeout, TimeUnit unit) 阻塞的时间,超过这个时间就不等待了.
注意点:
计时器必须大于等于0,只是等于0的时候,计时器就是0,调用await()方法时,不会阻塞当前线程,CountDownLatch不可能重新初始化或者修改CountDownLatch对象的内部 计数器的值,不可逆性.
Semaphore
semaphore(信号量)是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源.这个跟队列有点像,画图理解更直观,
代码:
发现我们并发下载的数量是只允许2个线程,当访问总线程大于2时,其他线程就要等待,Semaphore类常用的方法:
acquire():获取一个许可证,
release():释放许可证,这时候有多余的线程就加入到线程池中
tryAcquire():尝试获取许可证
intavailblePermits():返回此信号量当前可用的许可证数
intgetQueueLength():返回正在等待获取许可证的线程数
hasQueuedThreads():是否有线程正在等待获取许可证
reducePermits(int reduction):减少reduction个许可证,
getQueueThreads():返回所有等待获取许可证的线程集合
Semaphore类的构造函数中传入的数,表示同时并发访问控制在多少个线程.
Exchanger
exchanger是一个用于线程间协作的工具类,Exchanger用于进行线程间的数据交换,它提供一个同步点,在这个同步点,二个线程可以交换彼此的数据.在这二个线程通过exchange方法交换数据,如果第一个线程先执行exchange()方法,它会一直等待第二个线程也执行exchange方法,当二个线程都达到同步点时,这二个线程就可以交换数据,将本线程生产出来的数据传递给对方.
打个比方吧,发现我们那农村结婚是需要30,40万,这对一般的家庭确实是一个很大的钱,因为农村没读书的孩子多是 21~23之间就要讨老婆了,这么年轻自己打工赚到30万还是很少很少的,哪女孩一般会说等你赚到了30万马上给你结婚,于是只好苦逼的年复一年的工作,每个月把打工的钱给女孩,到了30万马上通知男方说可以结婚了,
现在用代码实现下:
运行结果:
Exchanger主要用于二个线程之间交换数据,注意,只能是2个线程,如果有一个线程没执行exchange()方法,则会一直等待,线程就处于阻塞状态了!如果怕一直等待,可以设置时间:exchange()有一个重载的方法.
exchange(V x, long timeout, TimeUnit unit)
CyclicBarrier
CyclicBarrierr如果你在翻译的话是关卡,栅栏的意思,也就是被卡住在哪里,还可以把这个词扯开成cyclic和barrier,cyclic是循环的意思,barrier是障碍的意思,从字面意思连起来读就是可循环的障碍,翻译成java语言就是当有几个线程同时去访问,要阻塞,直到最后一个线程达到屏障时,程序才会继续执行,
CyclicBarrier使用也很简单,首先它有2个构造函数:
public CyclicBarrier(int parties):parties一个int类型参数,表示屏障拦截的线程数量,
public CyclicBarrier(int parties,Runnable barrierAction),意思是在线程到达屏障时,优先执行barrierAction线程.
打个比方吧,你们主管带领小明,小王经过2个月的日夜工作,完成了一个重要的项目,领导准备奖励你们项目组一些钱,让你们出去玩,主管,小明,小王呢?经过思考,去xxx天上人间玩下吧,他们约好在万达商场见面,车子已经提前安排好了,所以只有三个人都到了,才上车.
来个图片赏析下:
代码如下:
假如在中途经理打电话来说也想去,那么三个人在等,但是到最后经理突然有急事,电话也没电了,没通知这三个哥们,那么这三个哥们也出去了,因为没有第四个线程去执行await()方法,那么之前的线程达到屏障时都不会往下执行了,也就是各自上车的行为不会执行,那还怎么去天上人间.
运行结果:
这就是这三个人被经理放了鸽子了,一直等待,等到天荒地老也到不了天上人间!直到天上人间被查了,呵呵.
CyclicBarrier有2个构造函数,第二个构造函数第二个参数传递的一个Runnable,意思这个线程先执行
现在有个生活场景,就是聚餐,一般聚餐之前领导都要先说几句然后才能开始吃,
代码如下:
运行结果:
现在讲下CyclicBarrier和CountDownLatch的区别:
CountDownLatch的计数器只能使用一次,而CyclicBarrier的计数器可以使用reset()方法重置,所以CyclicBarrier能处理的业务场景相对CountDownLatch更多,
今天下午没事干把买的java并发编程艺术这本书拿出来看了看,看了下也记不住,还是好记性不如烂笔头,今天讲四个并发中可能会用到的工具类,分别是:
CountDownLatch
CyclicBarrier
Semaphore
Exchanger
CountDownLatch
countDownLatch允许一个或多个线程等待其他线程完成操作.比如说有三个线程分别是老二,老大,老爸,这三个线程必须是老二吃好了,老大吃,老大吃完了,老爸吃,在20年钱,农村家里穷,一般有好吃的都是先留个最小的,然后才给其他兄弟姐妹吃,都不吃了,才由我们的父母吃,所以父母都不容易了,为了儿女,虽然是多个线程但是确实线性的,
我同事面试别人就问过好几次这个问题,在java或者android中常用的有2个方式实现
第一种方式:
使用jdk中Thread自带的函数join实现,join()用于当前执行线程等待join线程执行结束,其实实现原理是不停检查join线程是否存活,如果join线程存活则让当前线程永远等待,其中,wait(0)表示永远等待下去,join在jdk中的是实现方式如下:
/** * Waits for this thread to die. * * <p> An invocation of this method behaves in exactly the same * way as the invocation * * <blockquote> * {@linkplain #join(long) join}{@code (0)} * </blockquote> * * @throws InterruptedException * if any thread has interrupted the current thread. The * <i>interrupted status</i> of the current thread is * cleared when this exception is thrown. */ public final void join() throws InterruptedException { join(0); }直到join线程中止后,线程的this.notifyAll()方法会被调用,调用notifyAll()方法是在JVM里实现的,现在把上面的例子用代码实现下:
package com.thread; public class RunnableJob { public static void main(String[] args) throws InterruptedException { Worker runnableJob = new Worker(); Thread t1 = new Thread(runnableJob, "老二"); Thread t2 = new Thread(runnableJob, "老大"); Thread t3 = new Thread(runnableJob, "老爸"); t1.start(); t1.join(); t2.start(); t2.join(); t3.start(); t3.join(); System.out.println("主线程执行完毕----"); } } class Worker implements Runnable{ public void run() { Thread thread = Thread.currentThread(); try { Thread.sleep(5000); System.out.println(thread.getName()+"吃完了"); } catch (InterruptedException e) { e.printStackTrace(); } } }log:
CountDownLatch是一个同步计数器,现在有个需求,在古代是男人先吃饭,特别是有客人在的时候,女人不能上桌,等待男人吃完,女人才能上桌吃饭,
package com.thread; import java.util.concurrent.CountDownLatch; public class CountDownLatchTest { public static void main(String[] args) { final CountDownLatch countDownLatch = new CountDownLatch(3); new Thread("老二"){ public void run() { try { Thread.sleep(3000); System.out.println(Thread.currentThread().getName()+"吃完了"); countDownLatch.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } }; }.start(); new Thread("老大"){ public void run() { try { Thread.sleep(3000); System.out.println(Thread.currentThread().getName()+"吃完了"); countDownLatch.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } }; }.start(); new Thread("老爸"){ public void run() { try { Thread.sleep(3000); System.out.println(Thread.currentThread().getName()+"吃完了"); countDownLatch.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } }; }.start(); System.out.println("等待三个男人吃完,女人才能上桌吃饭,等...."); try { countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("女人们可以上桌吃饭了"); } }运行结果:
你会发现CountDownLatch和join还是有区别:
相同点:都能等待一个或者多个线程执行完成操作,比如等待三个线程执行完毕后,第四个线程才能执行
不同点:join能让线程按我们预想的的顺序执行,比如线程1执行完了,线程2才能执行,线程2执行完,线程3才能执行,但是CountDownLatch就做不到.
如果查看CountDownLatch源码,发现这源码还是很少的,没几行代码,方法也就几个:CountDownLatch的构造函数接受一个int类型的参数作为计时器,如果你想等待N个点完成,就在这里传入N
当我们调用CountDownLatch的countDown方法时,N就会减1,CountDownLatch的await方法会阻塞当前线程,直到N变为零(也就是线程都执行完了),由于countDown方法可以用在任何地方,所以这里说的N个点,可以是N个线程,也可以是1个线程里的N个执行步骤。用在多线程时,只需把这个CountDownLatch的引用传递到线程中即可,
当然阻塞还有一个重载的方法就是await(long timeout, TimeUnit unit) 阻塞的时间,超过这个时间就不等待了.
注意点:
计时器必须大于等于0,只是等于0的时候,计时器就是0,调用await()方法时,不会阻塞当前线程,CountDownLatch不可能重新初始化或者修改CountDownLatch对象的内部 计数器的值,不可逆性.
Semaphore
semaphore(信号量)是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源.这个跟队列有点像,画图理解更直观,
代码:
package com.thread; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; public class SemaphoreDemo { public static void main(String[] args) { ExecutorService service = Executors.newCachedThreadPool(); final Semaphore semaphore = new Semaphore(2); final int count = 5; for (int i = 0; i < count; i++) { Runnable runnable = new Runnable() { public void run() { try { semaphore.acquire(); System.out.println("线程:" + Thread.currentThread().getName()+"开始下载"); Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } finally { System.out.println("线程:" + Thread.currentThread().getName() + "下载完毕"); semaphore.release(); } } }; service.execute(runnable);//放在线程池中去维护 } service.shutdown(); } }运行结果:
发现我们并发下载的数量是只允许2个线程,当访问总线程大于2时,其他线程就要等待,Semaphore类常用的方法:
acquire():获取一个许可证,
release():释放许可证,这时候有多余的线程就加入到线程池中
tryAcquire():尝试获取许可证
intavailblePermits():返回此信号量当前可用的许可证数
intgetQueueLength():返回正在等待获取许可证的线程数
hasQueuedThreads():是否有线程正在等待获取许可证
reducePermits(int reduction):减少reduction个许可证,
getQueueThreads():返回所有等待获取许可证的线程集合
Semaphore类的构造函数中传入的数,表示同时并发访问控制在多少个线程.
Exchanger
exchanger是一个用于线程间协作的工具类,Exchanger用于进行线程间的数据交换,它提供一个同步点,在这个同步点,二个线程可以交换彼此的数据.在这二个线程通过exchange方法交换数据,如果第一个线程先执行exchange()方法,它会一直等待第二个线程也执行exchange方法,当二个线程都达到同步点时,这二个线程就可以交换数据,将本线程生产出来的数据传递给对方.
打个比方吧,发现我们那农村结婚是需要30,40万,这对一般的家庭确实是一个很大的钱,因为农村没读书的孩子多是 21~23之间就要讨老婆了,这么年轻自己打工赚到30万还是很少很少的,哪女孩一般会说等你赚到了30万马上给你结婚,于是只好苦逼的年复一年的工作,每个月把打工的钱给女孩,到了30万马上通知男方说可以结婚了,
现在用代码实现下:
package com.thread; import java.util.Random; import java.util.concurrent.Exchanger; public class ExchangerDemo { public static void main(String[] args) { Exchanger<Integer> exchanger = new Exchanger<>(); new Girl(exchanger).start(); new Man(exchanger).start(); } } /** * 男人 * @author admin */ class Man extends Thread { Exchanger<Integer> exchanger = null; public Man(Exchanger<Integer> exchanger) { super(); this.exchanger = exchanger; } @Override public void run() { Random rand = new Random(); int money = 0; for(int i=0;i<4;i++){ money+=100000;//年薪在10万以内 try { exchanger.exchange(money);//存钱 } catch (InterruptedException e) { e.printStackTrace(); } } } } /** * 女人 */ class Girl extends Thread { Exchanger<Integer> exchanger = null; int money = 0; public Girl(Exchanger<Integer> exchanger) { super(); this.exchanger = exchanger; } @Override public void run() { for(int i=0;i<4;i++){ try { money = exchanger.exchange(money) ; System.out.println(money>300000?"亲爱的"+money+"万我们可以结婚了":money+"块这么少,"+"臭屌丝活该单身,还不去赚钱娶老婆"); } catch (InterruptedException e) { e.printStackTrace(); } } } }
运行结果:
Exchanger主要用于二个线程之间交换数据,注意,只能是2个线程,如果有一个线程没执行exchange()方法,则会一直等待,线程就处于阻塞状态了!如果怕一直等待,可以设置时间:exchange()有一个重载的方法.
exchange(V x, long timeout, TimeUnit unit)
CyclicBarrier
CyclicBarrierr如果你在翻译的话是关卡,栅栏的意思,也就是被卡住在哪里,还可以把这个词扯开成cyclic和barrier,cyclic是循环的意思,barrier是障碍的意思,从字面意思连起来读就是可循环的障碍,翻译成java语言就是当有几个线程同时去访问,要阻塞,直到最后一个线程达到屏障时,程序才会继续执行,
CyclicBarrier使用也很简单,首先它有2个构造函数:
public CyclicBarrier(int parties):parties一个int类型参数,表示屏障拦截的线程数量,
public CyclicBarrier(int parties,Runnable barrierAction),意思是在线程到达屏障时,优先执行barrierAction线程.
打个比方吧,你们主管带领小明,小王经过2个月的日夜工作,完成了一个重要的项目,领导准备奖励你们项目组一些钱,让你们出去玩,主管,小明,小王呢?经过思考,去xxx天上人间玩下吧,他们约好在万达商场见面,车子已经提前安排好了,所以只有三个人都到了,才上车.
来个图片赏析下:
代码如下:
package thread; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; class Employee implements Runnable { private String name; private CyclicBarrier barrier; public Employee(String name, final CyclicBarrier barrier) { this.name = name; this.barrier = barrier; } public void run() { try { System.out.println(name+"已经到达万达商场等待"); barrier.await(); //等待最后一个线程到达(底层会去判断) } catch (InterruptedException | BrokenBarrierException e) { e.printStackTrace(); } System.out.println(name+"上车"); } } public class TestCyclicBarrier { public static void main(String[] args) { int num = 3; String[] strs = {"主管","小明","小王"}; CyclicBarrier barrier = new CyclicBarrier(num); for (int i = 0; i <num; i++) { new Thread(new Employee(strs[i], barrier)).start(); } } }运行结果:
假如在中途经理打电话来说也想去,那么三个人在等,但是到最后经理突然有急事,电话也没电了,没通知这三个哥们,那么这三个哥们也出去了,因为没有第四个线程去执行await()方法,那么之前的线程达到屏障时都不会往下执行了,也就是各自上车的行为不会执行,那还怎么去天上人间.
CyclicBarrier barrier = new CyclicBarrier(num+1);只要把这个构造函数传入的参数+1
运行结果:
这就是这三个人被经理放了鸽子了,一直等待,等到天荒地老也到不了天上人间!直到天上人间被查了,呵呵.
CyclicBarrier有2个构造函数,第二个构造函数第二个参数传递的一个Runnable,意思这个线程先执行
现在有个生活场景,就是聚餐,一般聚餐之前领导都要先说几句然后才能开始吃,
代码如下:
package thread; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; public class TestCyclicBarrierDemo { public static void main(String[] args) { int num = 5; CyclicBarrier barrier = new CyclicBarrier(num,new Leader()); for (int i = 0; i <num; i++) { new Thread(new Employee( barrier)).start(); } } } class Leader implements Runnable{ @Override public void run() { System.out.println("吃饭前我先说几句"); try { Thread.sleep(5000);//说了半个小时 比如吧 } catch (InterruptedException e) { e.printStackTrace(); } } } class Employee implements Runnable { private CyclicBarrier barrier; public Employee(final CyclicBarrier barrier) { this.barrier = barrier; } public void run() { try { System.out.println("都在等领导说完话准备吃"); barrier.await(); } catch (InterruptedException | BrokenBarrierException e) { e.printStackTrace(); } System.out.println("说了30分钟准备用筷子开始吃了"); } }
运行结果:
现在讲下CyclicBarrier和CountDownLatch的区别:
CountDownLatch的计数器只能使用一次,而CyclicBarrier的计数器可以使用reset()方法重置,所以CyclicBarrier能处理的业务场景相对CountDownLatch更多,
相关文章推荐
- java中常用的并发工具类
- Java并发中常用同步工具类
- java并发的艺术-读书笔记-第八章常用的并发工具类
- 常用java工具类
- 【工具类】Java常用数据类型的转换
- java开发中常用的数字工具类
- [JAVA][StringUtils]字符串工具类的常用方法
- 常用的java工具类
- 项目经验分享——Java常用工具类集合
- java中关于文件操作常用工具类
- Java多线程编程--(10)学习Java5.0 并发编程包--线程工具类
- 线程高级应用-心得6-java5线程并发库中同步工具类(synchronizers),新知识大用途
- Java数组操作的常用工具类
- 线程高级应用-心得6-java5线程并发库中同步工具类(synchronizers),新知识大用途
- 线程高级应用-心得8-java5线程并发库中同步集合Collections工具类的应用及案例分析
- java并发库之Executors常用的创建ExecutorService的几个方法说明
- NO.63 [file]IO常用工具类IOUtils(Java读文件、写文件、打Zip包)
- [JAVA][StringUtils]字符串工具类的常用方法
- 【Java常用工具类】之log4j 一: log4j超快速入门
- 黑马程序员_Java中的两个常用工具类及集合数组的相互转换