Java并发工具类---ForkJoin、countDownlatch、CyclicBarrier、Semaphore

一、Fork Join

fork join是JDK7引入的一种并发框架,采用分而治之的思想来处理并发任务

ForkJoin框架底层实现了工作窃取,当一个线程完成任务处于空闲状态时,会窃取其他工作线程的任务来做,这样可以充分利用线程来进行并行计算,减少线程竞争。但是在某些情况下也会存在竞争。

Fork Join框架局限性
1.拆分任务中不应该去执行IO操作
2.任务不能检查抛出异常,必须通过必要的代码来抛出异常。这个在源码中就可以体现,很多地方都是通过代码主动抛出异常。
3.任务只能使用Fork和Join操作来进行同步机制,如果使用了其他同步机制,则在同步操作时,工作线程就不能执行其他任务了。比如,在Fork/Join框架中,使任务进行了睡眠,那么,在睡眠期间内,正在执行这个任务的工作线程将不会执行其他任务了。

Demo:
ForkJoin进行累加计算

public class MakeArray {public static final int ARRAY_LENGTH=4000;//获取一个随机数的数组public static int[] makeArray(){Random r=new Random();int[] res=new int[ARRAY_LENGTH];for(int i=0;i<ARRAY_LENGTH;i++){res[i]=r.nextInt(ARRAY_LENGTH*3);}return res;}
}public class SumArray {private static class SumTask extends RecursiveTask<Integer>{private final static int THRESHOLD=MakeArray.ARRAY_LENGTH/10;private int[] src;private int fromIndex;private int toIndex;public SumTask(int[] src, int fromIndex, int toIndex) {this.src = src;this.fromIndex = fromIndex;this.toIndex = toIndex;}@Overrideprotected Integer compute() {if(toIndex-fromIndex<THRESHOLD){//无需再拆分int count=0;for(int i=fromIndex;i<=toIndex;i++){try {TimeUnit.MILLISECONDS.sleep(1);count+=src[i];} catch (InterruptedException e) {throw new RuntimeException(e);}}return count;}else{int mid=(fromIndex+toIndex)/2;SumTask left=new SumTask(src,fromIndex,mid);SumTask right=new SumTask(src,mid+1,toIndex);invokeAll(left,right); //执行任务,把任务添加到队列,该方法中执行了forkreturn left.join()+right.join(); //合并结果}}}public static void main(String[] args) {int[] src=MakeArray.makeArray();ForkJoinPool pool=new ForkJoinPool();SumTask innerFind=new SumTask(src,0,src.length-1);long start=System.currentTimeMillis();pool.invoke(innerFind);System.out.println("The count is "+innerFind.join()+" spend time:"+(System.currentTimeMillis()-start)+" ms");}
}

运行结果:
在这里插入图片描述
采用单线程进行对比:

public class SumNormal {public static void main(String[] args) {int count=0;int[] src=MakeArray.makeArray();long start=System.currentTimeMillis();for(int i=0;i<src.length;i++){try {TimeUnit.MILLISECONDS.sleep(1);count+=src[i];} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println("The count is "+count+" spend time:"+(System.currentTimeMillis()-start)+"ms");}
}

在这里插入图片描述

二、countDownlatch

在这里插入图片描述
countDownlatch也是一个java的同步工具类,它通过计数器来控制线程的执行顺序。初始化时需要初始化计数器的值,一般都是线程数量。每当一个线程执行完任务,计数器减一,当计数器为0,等待的线程就可以恢复执行任务。

需注意: 计数器的值不一定就是线程数量,线程中可以多次调用countDown来使计数器减一。
执行减一操作后,线程不一定要终止,也可以继续执行任务(如上图Ta,Td)。

Demo

public class UseCountDownLatch {//计数器设置为6static CountDownLatch latch=new CountDownLatch(6);private static class InitThread implements Runnable{@Overridepublic void run() {System.out.println("Thread_"+Thread.currentThread().getId()+" ready init work...");//计数器减1latch.countDown();for(int i=0;i<2;i++){System.out.println("Thread_"+Thread.currentThread().getId()+"......continue do its work");}}}private static class BusiThread implements Runnable{@Overridepublic void run() {try {//在此处会阻塞,当计数器扣减为0时会被唤醒latch.await();} catch (InterruptedException e) {throw new RuntimeException(e);}for(int i=0;i<3;i++){System.out.println("BusiThread_"+Thread.currentThread().getId()+" do business----");}}}public static void main(String[] args) {new Thread(new Runnable() {@Overridepublic void run() {try {TimeUnit.MILLISECONDS.sleep(1);System.out.println("Thread_"+Thread.currentThread().getId()+" ready init work step 1st...");latch.countDown();System.out.println("begin step 2nd....");TimeUnit.MILLISECONDS.sleep(1);System.out.println("Thread_"+Thread.currentThread().getId()+" ready init work step 2nd...");latch.countDown();} catch (InterruptedException e) {throw new RuntimeException(e);}}}).start();new Thread(new BusiThread()).start();for(int i=0;i<=3;i++){new Thread(new InitThread()).start();}try {latch.await();System.out.println("Main do ites work ...");} catch (InterruptedException e) {throw new RuntimeException(e);}}
}

运行结果:
在这里插入图片描述

三、CyclicBarrier

CyclicBarrier可以实现让一组线程达到一个屏障(Barrier)时被阻塞,当所有线程都到达屏障时,被阻塞的线程才会继续执行
Demo

public class UseCyclicBarrier {//屏障拦截四个线程,当屏障放开时,会执行传入的CollectThreadprivate static CyclicBarrier barrier=new CyclicBarrier(4,new CollectThread());//存储子线程的工作结果private static ConcurrentHashMap<String,Long> resultmap=new ConcurrentHashMap<>();public static void main(String[] args) {for(int i=0;i<=3;i++){new Thread(new SubThread()).start();}}private static class CollectThread implements Runnable{@Overridepublic void run() {StringBuilder res=new StringBuilder();for(Map.Entry<String,Long> r:resultmap.entrySet()){res.append("["+r.getValue()+"]");}System.out.println("the result ="+res);}}private static class SubThread implements Runnable{@Overridepublic void run() {long id=Thread.currentThread().getId();resultmap.put(Thread.currentThread().getId()+"",id);System.out.println("Thread_"+id+"...do something");try {//在此处被屏障拦截,当屏障放开后才会继续执行barrier.await();System.out.println("Thread_"+id+"...do its business");} catch (InterruptedException e) {throw new RuntimeException(e);} catch (BrokenBarrierException e) {throw new RuntimeException(e);}}}}

结果:
在这里插入图片描述

四、Semaphore

Semaphore的中文翻译就是信号量,是用来进行流量控制的,可以协调各个线程合理的使用资源。
new Semaphore(10) 来创建一个信号量,值为10,这里会创建一个非公平的锁的同步阻塞队列。
acquire方法信号量-1 release方法信号量+1 信号量为0时再执行acquire就会阻塞,直到信号量不为0时(其他线程执行了release)才会继续运行

1.Semaphore实现连接池

注意,实现连接池时需要用两个Semaphore,因为通过release归还时,信号量会超出10个的限制

public class DBPoolSemaphore {private final static int POOL_SIZE=10;//可用连接和已用连接private final Semaphore useful,useless;//存放数据库连接的容器private static LinkedList<Connection> pool=new LinkedList<>();public DBPoolSemaphore() {this.useful=new Semaphore(10);this.useless=new Semaphore(0);for(int i=0;i<POOL_SIZE;i++){pool.addLast(SqlConnectImpl.fetchConnection());}}//归还连接public void returnConnect(Connection connection) throws InterruptedException {if(connection!=null){System.out.println("There are now"+useful.getQueueLength()+"threads waiting to connection "+"useful connection:"+ useful.availablePermits());useless.acquire();synchronized (pool){pool.addLast(connection);}useful.release();}}//获取连接public Connection getConnect() throws InterruptedException {useful.acquire();Connection connection;synchronized (pool){connection=pool.removeFirst();}useless.release();return connection;}
}public class AppTest {private static DBPoolSemaphore dbPool=new DBPoolSemaphore();private static class BusiThread extends Thread{@Overridepublic void run() {Random r=new Random();long start=System.currentTimeMillis();try {Connection connection= dbPool.getConnect();System.out.println("Thread_"+Thread.currentThread().getId()+"get db connection use time:"+(System.currentTimeMillis()-start)+"ms");TimeUnit.MILLISECONDS.sleep(100+r.nextInt(100)); //模拟业务操作System.out.println("task completion,return connection");dbPool.returnConnect(connection);} catch (InterruptedException e) {throw new RuntimeException(e);}}}public static void main(String[] args) {for(int i=0;i<50;i++){Thread thread=new BusiThread();thread.start();}}
}

运行结果:
在这里插入图片描述
在这里插入图片描述

2.思考

使用双信号量是为了防止信号量会超过10个的限制,如果按如下的方法调用连接池:

public class AppTest {private static DBPoolSemaphore dbPool=new DBPoolSemaphore();private static class BusiThread extends Thread{@Overridepublic void run() {Random r=new Random();long start=System.currentTimeMillis();try {
//				Connection connection= dbPool.getConnect();
//				System.out.println("Thread_"+Thread.currentThread().getId()+
//					"get db connection use time:"+(System.currentTimeMillis()-start)+"ms");
//				TimeUnit.MILLISECONDS.sleep(100+r.nextInt(100)); //模拟业务操作
//				System.out.println("task completion,return connection");dbPool.returnConnect(new SqlConnectImpl());} catch (InterruptedException e) {throw new RuntimeException(e);}}}public static void main(String[] args) {for(int i=0;i<50;i++){Thread thread=new BusiThread();thread.start();}}
}

在线程中,只归还连接,归还的是自己new出来的连接。如果此时是单信号量只有useful,那么useful会变成60个:
在这里插入图片描述

    //单信号量public void returnConnect(Connection connection) throws InterruptedException {if(connection!=null) {System.out.println("There are now"+useful.getQueueLength()+"threads waiting to connection "+"useful connection:"+ useful.availablePermits());synchronized (pool) {pool.addLast(connection);}useful.release();}}

如果采用两个信号量,因为useless一开始为0,所以没有get连接直接归还连接时,会在useless.acquire那里阻塞住,可以有效的防止上面情况的发生。

	//双信号量public void returnConnect(Connection connection) throws InterruptedException {if(connection!=null){System.out.println("There are now"+useful.getQueueLength()+"threads waiting to connection "+"useful connection:"+ useful.availablePermits());System.out.println("1");useless.acquire(); //useless一开始为0.直接调用returnConnect会在这里阻塞住System.out.println("2");synchronized (pool){pool.addLast(connection);}useful.release();}}

在这里插入图片描述
log中并没有2,归还连接时被阻塞在useless.acquire

总之,双信号量可以有效的防止可用连接溢出的情况发生。个人感觉,如果是实现一个线程池,线程池中的连接不能让用户通过new SqlConnectImpl()这种形式new出来,SqlConnectImpl应该是对用户不可见的。对于用户来说,应该只能通过getConnect来从线程池获取连接,这样或许也能够避免这种问题出现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/240897.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

系列十四、SpringBoot + JVM参数配置实战调优

一、SpringBoot JVM参数配置实战调优 1.1、概述 前面的系列文章大篇幅的讲述了JVM的内存结构以及各种参数&#xff0c;今天就使用SpringBoot项目实战演示一下&#xff0c;如何进行JVM参数调优&#xff0c;如果没有阅读过前面系列文章的朋友&#xff0c;建议先阅读后再看本篇文…

Java整合APNS推送消息-IOS-APP(基于.p12推送证书)

推送整体流程 1.在开发者中心申请对应的证书&#xff08;我用的是.p12文件&#xff09; 2.苹果手机用户注册到APNS&#xff0c;APNS将注册的token返回给APP&#xff08;服务端接收使用&#xff09;。 3.后台服务连接APNS&#xff0c;获取连接对象 4.后台服务构建消息载体 5.后台…

Searching for MobileNetV3(2019)

文章目录 Abstract主要内容实验结果 IntroductionRelated WorkEfficient Mobile Building BlocksNetwork SearchPlatform-Aware NAS for Block-wise SearchNetAdapt for Layer-wise Search Network ImprovementsRedesigning Expensive LayersNonlinearitiesLarge squeeze-and-e…

PHP-Xlswriter高性能导出Excel

使用背景 使用传统的PHPExcel导出效率太慢&#xff0c;并且资源占用高&#xff0c;数据量大的情况&#xff0c;会导致服务占用大量的资源&#xff0c;从而导致生产意味&#xff0c;再三思索后&#xff0c;决定使用其他高效率的导出方式 PHP-Xlswriter PHPExcel 因为内存消耗过…

信号与线性系统翻转课堂笔记9——傅里叶变换

信号与线性系统翻转课堂笔记9——傅里叶变换 The Flipped Classroom9 of Signals and Linear Systems 对应教材&#xff1a;《信号与线性系统分析&#xff08;第五版&#xff09;》高等教育出版社&#xff0c;吴大正著 一、要点 &#xff08;1&#xff0c;重点&#xff09;…

MyBatis中延迟加载,全局和局部的开启使用与关闭

文章目录 MyBatis中延迟加载&#xff0c;全局和局部的开启使用与关闭1、问题提出2、延迟加载和立即加载延迟加载立即加载 3、三种对应的表关系中的加载4、打开全局延迟加载&#xff08;实现一对一的延迟加载&#xff09;5、实现一对多的延迟加载&#xff08;将上面设置的全局延…

零成本搭建一款博客网站(基于Vercel+Hexo完美实现)【保姆级教程】

文章目录 &#x1f438;基于VercelHexo零成本搭建博客网站&#x1f43b;实现思路 &#x1f42e;Hexo的配置与安装&#x1f412;Hexo的美化与使用&#x1f42b;Github的推送与部署&#x1f43c;Vercel部署与网站上线&#x1f41b;总结 &#x1f438;基于VercelHexo零成本搭建博客…

【数据结构】递归与分治

一.递归 1.递归的概念&#xff1a; 子程序&#xff08;或函数&#xff09;. 接调用自己或通过一系列调用语句间接调用自己&#xff0c;成为递归。 递归是一种描述问题和解决问题的基本方法。 重复地把问题转化为与原问题相似的新问题&#xff0c;直到问题解决为止。 2.递归…

ElasticSearch学习篇9_文本相似度计算方法现状以及基于改进的 Jaccard 算法代码实现

背景 XOP亿级别题库的试题召回以及搜题的举一反三业务场景都涉及使用文本相似搜索技术&#xff0c;学习此方面技术以便更好的服务于业务场景。 目前基于集合的Jaccard算法以及基于编辑距离的Levenshtein在计算文本相似度场景中有着各自的特点&#xff0c;为了优化具体的计算时…

【Linux驱动】字符设备驱动程序框架 | LED驱动

&#x1f431;作者&#xff1a;一只大喵咪1201 &#x1f431;专栏&#xff1a;《RTOS学习》 &#x1f525;格言&#xff1a;你只管努力&#xff0c;剩下的交给时间&#xff01; 目录 &#x1f3c0;Hello驱动程序⚽驱动程序框架⚽编程 &#x1f3c0;LED驱动⚽配置GPIO⚽编程驱动…

YB75XXH系列是采用CMOS工艺制造,低功耗的高压稳压器

YB75xxH 高耐压线性稳压器 ■产品简介&#xff1a; YB75XXH系列是采用CMOS工艺制造&#xff0c;低功耗的高压稳压器&#xff0c;最高输入电压可达25V,输出电压范围为1.5V一12.0V。它具有高精度的输出电压、极低的供电电流、极低的跌落电压等特点。 ■产品特点&#xff1a; …

SpringBoot 3 集成Hive 3

前提条件: 运行环境&#xff1a;Hadoop 3.* Hive 3.* MySQL 8 &#xff0c;如果还未安装相关环境&#xff0c;请参考&#xff1a;Hive 一文读懂 Centos7 安装Hadoop3 单机版本&#xff08;伪分布式版本&#xff09; SpringBoot 2 集成Hive 3 pom.xml <?xml ver…

MyBatis 通过 SqlSession 实现动态Entity批量插入

需要几个关键点: 1、entity对应的service需要继承BaseService 2、entity对应的serviceImpl需要实现baseMapper方法&#xff0c;需要把当前的mapper返回去 3、entity对应的Mapper需要BaseMapper

又是阿里,通义灵码免费平替GitHub Copilot

毫无疑问&#xff0c;人工智能已经在影响着我们日常生活的方方面面&#xff0c;同样的在软件开发领域&#xff0c;AI正在改变我们的开发方式。在软件开发领域&#xff0c;尽管有许多强大的AI编码工具&#xff0c;但国产&#xff0c;免费&#xff0c;使用门槛低&#xff0c;用起…

机器学习算法(11)——集成技术(Boosting——梯度提升)

一、说明 在在这篇文章中&#xff0c;我们学习了另一种称为梯度增强的集成技术。这是我在机器学习算法集成技术文章系列中与bagging一起介绍的一种增强技术。我还讨论了随机森林和 AdaBoost 算法。但在这里我们讨论的是梯度提升&#xff0c;在我们深入研究梯度提升之前&#xf…

linux buffer的回写的触发链路

mark_buffer_dirty中除了会标记dirty到buffer_head->state、page.flag、folio->mapping->i_pages外&#xff0c;还会调用inode所在文件系统的dirty方法&#xff08;inode->i_sb->s_op->dirty_inode&#xff09;。然后为inode创建一个它所在memory group的wri…

(十七)Flask之大型项目目录结构示例【二扣蓝图】

大型项目目录结构&#xff1a; 问题引入&#xff1a; 在上篇文章讲蓝图的时候我给了一个demo项目&#xff0c;其中templates和static都各自只有一个&#xff0c;这就意味着所有app的模板和静态文件都放在了一起&#xff0c;如果项目比较大的话&#xff0c;这就非常乱&#xf…

阿里云吴结生:云计算是企业实现数智化的阶梯

云布道师 近年来&#xff0c;越来越多人意识到&#xff0c;我们正处在一个数据爆炸式增长的时代。IDC 预测 2027 年全球产生的数据量将达到 291 ZB&#xff0c;与 2022 年相比&#xff0c;增长了近 2 倍。其中 75% 的数据来自企业&#xff0c;每一个现代化的企业都是一家数据公…

大创项目推荐 深度学习+python+opencv实现动物识别 - 图像识别

文章目录 0 前言1 课题背景2 实现效果3 卷积神经网络3.1卷积层3.2 池化层3.3 激活函数&#xff1a;3.4 全连接层3.5 使用tensorflow中keras模块实现卷积神经网络 4 inception_v3网络5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; *…

Excel 获取当前行的行数

ROW() 获取当前行 ROW()1 获取当前行然后支持二次开发