博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
通过java.util.concurrent写多线程程序
阅读量:4556 次
发布时间:2019-06-08

本文共 5085 字,大约阅读时间需要 16 分钟。

在JDK 1.5之前,要实现多线程的功能,得用到Thread这个类,通过这个类设计多线程程序,需要考虑性能,死锁,资源等很多因素,一句话,就是相当麻烦,而且很容易出问题。所幸的是,在JDK1.5之后,java.util.concurrent包出现了,这是一个设计良好的多线程工具类,本文就将介绍该类的基本使用方法。
按照本博的风格,依然是先扔上一段示例代码,然后我们再慢慢讲解
package com.giantray;import java.util.concurrent.CountDownLatch;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class TestConcurrent {             public static ExecutorService exec =  Executors.newCachedThreadPool();             public void startMutilThread()      {             int taskNum = 2;             final CountDownLatch cd = new CountDownLatch(taskNum );                          //任务1             exec.submit(new Runnable(){                   @Override                   public void run()                  {                        System. out.println("1 start" );                         try {                              Thread. sleep(3000);                        } catch (InterruptedException e) {                              e.printStackTrace();                        }                        cd.countDown();                        System. out.println("1 end" );                  }                              });                         //任务2             exec.submit(new Runnable(){                   @Override                   public void run()                  {                        System. out.println("2 start" );                        cd.countDown();                        System. out.println("2 end" );                  }                              });                  System. out.println("submit end" );             try            {                  cd.await();            }             catch (InterruptedException e)            {                              }            System. out.print("end" );      }       public static void main(String[] args) {            TestConcurrent testThread = new TestConcurrent();            testThread.startMutilThread();      }}
输出
1 startsubmit end2 start2 end1 endend
下面进行详细介绍:

一、使用流程

1、ExecutorService exec =  Executors.newCachedThreadPool();获得一个线程池
2、final CountDownLatch cd = new CountDownLatch(taskNum ); 定义计数器,指定要并发执行的任务数
3、 exec .submit( new Runnable(){};定义任务的执行逻辑
4、cd.countDown();任务结束了,要让计算器减1
5、cd.await();表示等待计数器小于等于0时,再继续往下走其他逻辑

二、线程池的管理

除了示例中的newCachedThreadPool(),也有其他各种类型的线程池:
1、newFixedThreadPool(固定大小线程池)
线程数是固定的,例如线程数设为1,则需要等待任务1结束后,才执行任务2;如设为2,则两个任务能同时执行
2、newCachedThreadPool(无限制大小的线程池)
线程数无固定大小,当前无可用线程,就回创建新线程。如果任务1没结束,那么启动任务2时,就会新建线程,这时线程池大小为2;但假如启动任务2时,任务1已结束,则不会新建线程,会沿用任务1使用的线程
3、newSingleThreadExecutor(单线程)
使用单线程池时,就没办法同时执行多个任务,因此,本例中的任务1执行完后,才会执行任务2
4、newScheduledThreadPool(定时任务线程池)
支持定时任务的线程池,与Timer,Quartz一样,常见于一些定时任务调度程序·

三、计数器CountDownLatch

1、示例讲解

在示例中,我们通过多线程执行了两个任务。之后我们需要知道,两个任务是否都执行结束?都执行结束了,我们才能继续执行接下来的其他逻辑。为了这个目的,需要引入CountDownLatch这个计数器。先声明一个计算量为TaskNum(2)的计数器,接着,每个任务执行结束时,要执行countDown()方法,而最后的await()方法表示,如果CountDownLatch不为零,那么就在这里等待,下面的逻辑会被阻塞住,不会继续执行,等待两个任务都执行结束了--执行两次countDown()方法,计数器等于0,这时候程序才会继续往下执行System. out.print("end")这行代码

2、它的兄弟CyclicBarrier

还有一个类似CountDownLatch的计数器,名为CyclicBarrier。这两者的区别:CountDownLatch被减至0后,是不能重置的。而CyclicBarrier被减至0后,会自动恢复至初始值,因此它是一个循环的计数器,举一个例子,运动员跑步,这是一个任务,而这个任务,又分为运动员准备,以及开跑两个子任务,所有的运动员,必须等待其他运动员都”准备“好后,才能起跑。为了实现这个逻辑,及可以用到CyclicBarrier,CyclicBarrier的计数初始值为运动员数量,运动员准备完毕时,让计数器减1,这时候它不会进行”开跑“,等到计数器减为0时,所有运动员才同时“起跑”,并且会重新恢复至初始值,等到所有运动员都跑完时,计数器才又恢复为0,这时候,才能再继续执行其他任务。而如果使用CyclicBarrier,在线程执行过程中,是不能等待其他线程的,不能等其他线程都“准备”好了,所有线程再一起“开跑”

四、并发执行

多运行几次示例程序,会发现,有时候,"2 start"会早于"1 start"输出,因此,不一定是先submit的任务,就先“开始执行”。这也告诉我们,不能想当然地以为,任务1先submit,就可以在任务1里做一些全局初始化的工作,然后在任务2里可以去拿任务1初始化的变量。

五、线程池的生命周期管理

1、三种基本状态

线程池的声明周期有三个状态,运行,关闭,终止。初始化线程时,处于运行状态,执行shutdown()方法后,处于关闭状态,但这时候线程任务会继续执行,当所有任务都结束后,才会变成终止状态。

2、shutdown()与shutdownNow()的区别

这两个方法都用于关闭线程池
shutdown() 关闭线程池,但之前提交的任务,会继续执行;如果新提交任务,会抛异常
shutdownNow() 马上关闭线程池,之前提交的任务,如果还未执行完,会被终止,且抛出异常

3、shutdown()的作用

  •   释放线程池资源(其实不关闭线程池,会资源会有多大的消耗,笔者也不清楚)
  •   可以让程序有序退出。这是什么意思呢?讲一个例子,你就明白了。假如你的程序正在并发执行任务,而且还一直会提交新的任务。这时候,该如何设计程序的“退出”功能呢?首先,为了任务的原子性,希望能执行完已提交的任务,并且阻止新的任务提交。这时候,就得用到了shutdown()了。退出功能的代码可以是这样:
System.out.println("准备退出程序");service.shutdown();while (true) {     try {          System.out.println("等待“已提交任务”执行结束");          //awaitTerminationm,先等待5秒,再检查任务是否结束          if (service.awaitTermination(5, TimeUnit.SECONDS)) {               break;          }     } catch (InterruptedException e) {     }}System.out.println("”已提交任务“执行完,程序可以安全退出");

六、获取线程任务的执行结果

如果想知道任务的执行结果,就得使用Future和Callable。Future可以拿到结果,而Callable能返回值,用于产生结果
对示例代码做下修改,其中,task1.get()将拿到call()方法的return值,也就是示例中的"false"
int taskNum = 1;final CountDownLatch cd = new CountDownLatch(taskNum);Future
task1 = exec.submit(new Callable
(){ @Override public Boolean call() throws Exception { System.out.println("1 start"); cd.countDown(); System.out.println("1 end"); return false; } });try { cd.await(); Boolean result = task1.get(); System.out.println("result : " + result);} catch (InterruptedException e) { e.printStackTrace();} catch (ExecutionException e) { e.printStackTrace();}System.out.print("end");

 

转载于:https://www.cnblogs.com/jiangu66/p/3239134.html

你可能感兴趣的文章
centos6.3下如何搭建LAMP环境
查看>>
C#的一些基础内容
查看>>
nodejs概述
查看>>
H3C PAP验证配置示例
查看>>
oracle-Dbca数据库模板
查看>>
ionic 轮播
查看>>
Faster-RCNN Pytorch实现的minibatch包装
查看>>
[LeetCode] 168. Excel Sheet Column Title_Easy tag: Math
查看>>
[Ionic] Build and Run an Ionic App from Scratch
查看>>
纯手写wcf代码,wcf入门,wcf基础教程
查看>>
关于8.0.15版本的mysql下载与安装
查看>>
Redis主从复制看这篇就够了
查看>>
部署和调优 2.3 tomcat中JDK安装
查看>>
洛谷 P1202 [USACO1.1]黑色星期五Friday the Thirteenth 题解
查看>>
(4.20)SQL Server数据库启动过程,以及启动不起来的各种问题的分析及解决技巧...
查看>>
基本数据类型(数字和字符串)
查看>>
函数__装饰器
查看>>
linux system函数分析
查看>>
前端优化措施
查看>>
论学习汉语和学习编程的异同点
查看>>