全网整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:400-708-3566

Java 线程池原理深入分析

Java 线程池原理

Executor框架的两级调度模型

在HotSpot VM的模型中,Java线程被一对一映射为本地操作系统线程。JAVA线程启动时会创建一个本地操作系统线程,当JAVA线程终止时,对应的操作系统线程也被销毁回收,而操作系统会调度所有线程并将它们分配给可用的CPU。

在上层,JAVA程序会将应用分解为多个任务,然后使用应用级的调度器(Executor)将这些任务映射成固定数量的线程;在底层,操作系统内核将这些线程映射到硬件处理器上。

Executor框架类图

在前面介绍的JAVA线程既是工作单元,也是执行机制。而在Executor框架中,我们将工作单元与执行机制分离开来。Runnable和Callable是工作单元(也就是俗称的任务),而执行机制由Executor来提供。这样一来Executor是基于生产者消费者模式的,提交任务的操作相当于生成者,执行任务的线程相当于消费者。

1、从类图上看,Executor接口是异步任务执行框架的基础,该框架能够支持多种不同类型的任务执行策略。

public interface Executor {

  void execute(Runnable command);
}

Executor接口就提供了一个执行方法,任务是Runnbale类型,不支持Callable类型。

2、ExecutorService接口实现了Executor接口,主要提供了关闭线程池和submit方法:

public interface ExecutorService extends Executor {

  List<Runnable> shutdownNow();


  boolean isTerminated();


  <T> Future<T> submit(Callable<T> task);

 }

另外该接口有两个重要的实现类:ThreadPoolExecutor与ScheduledThreadPoolExecutor。

其中ThreadPoolExecutor是线程池的核心实现类,用来执行被提交的任务;而ScheduledThreadPoolExecutor是一个实现类,可以在给定的延迟后运行任务,或者定期执行命令。

在上一篇文章中,我是使用ThreadPoolExecutor来通过给定不同的参数从而创建自己所需的线程池,但是在后面的工作中不建议这种方式,推荐使用Exectuors工厂方法来创建线程池

这里先来区别线程池和线程组(ThreadGroup与ThreadPoolExecutor)这两个概念:

a、线程组就表示一个线程的集合。

b、线程池是为线程的生命周期开销问题和资源不足问题提供解决方案,主要是用来管理线程。

Executors可以创建3种类型的ThreadPoolExecutor:SingleThreadExecutor、FixedThreadExecutor和CachedThreadPool

a、SingleThreadExecutor:单线程线程池

ExecutorService threadPool = Executors.newSingleThreadExecutor();

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
      (new ThreadPoolExecutor(1, 1,
                  0L, TimeUnit.MILLISECONDS,
                  new LinkedBlockingQueue<Runnable>()));
  }

我们从源码来看可以知道,单线程线程池的创建也是通过ThreadPoolExecutor,里面的核心线程数和线程数都是1,并且工作队列使用的是无界队列。由于是单线程工作,每次只能处理一个任务,所以后面所有的任务都被阻塞在工作队列中,只能一个个任务执行。

b、FixedThreadExecutor:固定大小线程池

ExecutorService threadPool = Executors.newFixedThreadPool(5);

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                   0L, TimeUnit.MILLISECONDS,
                   new LinkedBlockingQueue<Runnable>());
  }

这个与单线程类似,只是创建了固定大小的线程数量。

c、CachedThreadPool:无界线程池

ExecutorService threadPool = Executors.newCachedThreadPool();

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                   60L, TimeUnit.SECONDS,
                   new SynchronousQueue<Runnable>());
  }

无界线程池意味着没有工作队列,任务进来就执行,线程数量不够就创建,与前面两个的区别是:空闲的线程会被回收掉,空闲的时间是60s。这个适用于执行很多短期异步的小程序或者负载较轻的服务器。

Callable、Future、FutureTash详解

Callable与Future是在JAVA的后续版本中引入进来的,Callable类似于Runnable接口,实现Callable接口的类与实现Runnable的类都是可以被线程执行的任务。

三者之间的关系:

Callable是Runnable封装的异步运算任务。

Future用来保存Callable异步运算的结果

FutureTask封装Future的实体类

1、Callable与Runnbale的区别

a、Callable定义的方法是call,而Runnable定义的方法是run。

b、call方法有返回值,而run方法是没有返回值的。

c、call方法可以抛出异常,而run方法不能抛出异常。

2、Future

Future表示异步计算的结果,提供了以下方法,主要是判断任务是否完成、中断任务、获取任务执行结果

public interface Future<V> {
 
   boolean cancel(boolean mayInterruptIfRunning);
 
   boolean isCancelled();
 
   boolean isDone();
 
   V get() throws InterruptedException, ExecutionException;
 
   V get(long timeout, TimeUnit unit)
     throws InterruptedException, ExecutionException, TimeoutException;
 }

3、FutureTask

可取消的异步计算,此类提供了对Future的基本实现,仅在计算完成时才能获取结果,如果计算尚未完成,则阻塞get方法。

public class FutureTask<V> implements RunnableFuture<V>

public interface RunnableFuture<V> extends Runnable, Future<V>

FutureTask不仅实现了Future接口,还实现了Runnable接口,所以不仅可以将FutureTask当成一个任务交给Executor来执行,还可以通过Thread来创建一个线程。

Callable与FutureTask

定义一个callable的任务:

public class MyCallableTask implements Callable<Integer>
 {
   @Override
   public Integer call()
     throws Exception
   {
     System.out.println("callable do somothing");
     Thread.sleep(5000);
     return new Random().nextInt(100);
   }
 }

public class CallableTest
 {
   public static void main(String[] args) throws Exception
   {
     Callable<Integer> callable = new MyCallableTask();
     FutureTask<Integer> future = new FutureTask<Integer>(callable);
     Thread thread = new Thread(future);
     thread.start();
     Thread.sleep(100);
     //尝试取消对此任务的执行
     future.cancel(true);
     //判断是否在任务正常完成前取消
     System.out.println("future is cancel:" + future.isCancelled());
     if(!future.isCancelled())
     {
       System.out.println("future is cancelled");
     }
     //判断任务是否已完成
     System.out.println("future is done:" + future.isDone());
     if(!future.isDone())
     {
       System.out.println("future get=" + future.get());
     }
     else
     {
       //任务已完成
       System.out.println("task is done");
     }
   }
 }

执行结果:

callable do somothing
future is cancel:true
future is done:true
task is done

这个DEMO主要是通过调用FutureTask的状态设置的方法,演示了状态的变迁。

a、第11行,尝试取消对任务的执行,该方法如果由于任务已完成、已取消则返回false,如果能够取消还未完成的任务,则返回true,该DEMO中由于任务还在休眠状态,所以可以取消成功。

future.cancel(true);

b、第13行,判断任务取消是否成功:如果在任务正常完成前将其取消,则返回true

System.out.println("future is cancel:" + future.isCancelled());

c、第19行,判断任务是否完成:如果任务完成,则返回true,以下几种情况都属于任务完成:正常终止、异常或者取消而完成。

我们的DEMO中,任务是由于取消而导致完成。

 System.out.println("future is done:" + future.isDone());

d、在第22行,获取异步线程执行的结果,我这个DEMO中没有执行到这里,需要注意的是,future.get方法会阻塞当前线程, 直到任务执行完成返回结果为止。

System.out.println("future get=" + future.get());

Callable与Future

public class CallableThread implements Callable<String>
{
  @Override
  public String call()
    throws Exception
  {
    System.out.println("进入Call方法,开始休眠,休眠时间为:" + System.currentTimeMillis());
    Thread.sleep(10000);
    return "今天停电";
  }

  public static void main(String[] args) throws Exception
  {
    ExecutorService es = Executors.newSingleThreadExecutor();
    Callable<String> call = new CallableThread();
    Future<String> fu = es.submit(call);
    es.shutdown();
    Thread.sleep(5000);
    System.out.println("主线程休眠5秒,当前时间" + System.currentTimeMillis());
    String str = fu.get();
    System.out.println("Future已拿到数据,str=" + str + ";当前时间为:" + System.currentTimeMillis());
  }
}

执行结果:

进入Call方法,开始休眠,休眠时间为:1478606602676
主线程休眠5秒,当前时间1478606608676
Future已拿到数据,str=今天停电;当前时间为:1478606612677

这里的future是直接扔到线程池里面去执行的。由于要打印任务的执行结果,所以从执行结果来看,主线程虽然休眠了5s,但是从Call方法执行到拿到任务的结果,这中间的时间差正好是10s,说明get方法会阻塞当前线程直到任务完成。

通过FutureTask也可以达到同样的效果:

public static void main(String[] args) throws Exception
  {
   ExecutorService es = Executors.newSingleThreadExecutor();
   Callable<String> call = new CallableThread();
   FutureTask<String> task = new FutureTask<String>(call);
   es.submit(task);
   es.shutdown();
   Thread.sleep(5000);
   System.out.println("主线程等待5秒,当前时间为:" + System.currentTimeMillis());
   String str = task.get();
   System.out.println("Future已拿到数据,str=" + str + ";当前时间为:" + System.currentTimeMillis());
  }

以上的组合可以给我们带来这样的一些变化:

如有一种场景中,方法A返回一个数据需要10s,A方法后面的代码运行需要20s,但是这20s的执行过程中,只有后面10s依赖于方法A执行的结果。如果与以往一样采用同步的方式,势必会有10s的时间被浪费,如果采用前面两种组合,则效率会提高:

1、先把A方法的内容放到Callable实现类的call()方法中

2、在主线程中通过线程池执行A任务

3、执行后面方法中10秒不依赖方法A运行结果的代码

4、获取方法A的运行结果,执行后面方法中10秒依赖方法A运行结果的代码

这样代码执行效率一下子就提高了,程序不必卡在A方法处。

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!


# Java  # 线程池原理  # 线程池原理详解  # 线程池原理分析  # Java 阻塞队列和线程池原理分析  # 详细分析JAVA 线程池  # Java线程池的应用实例分析  # Java线程池用法实战案例分析  # 详解Java线程池和Executor原理的分析  # Java线程池的分析和使用详解  # 时间为  # 操作系统  # 单线程  # 的是  # 都是  # 实现了  # 无界  # 创建一个  # 抛出  # 返回值  # 主要是  # 是一个  # 法会  # 我是  # 是在  # 会有  # 还可以  # 还在  # 多个  # 如有 


相关文章: 如何快速配置高效服务器建站软件?  北京的网站制作公司有哪些,哪个视频网站最好?  高防服务器租用首荐平台,企业级优惠套餐快速部署  建站之星导航配置指南:自助建站与SEO优化全解析  如何选择适配移动端的WAP自助建站平台?  如何通过网站建站时间优化SEO与用户体验?  如何在香港服务器上快速搭建免备案网站?  头像制作网站在线制作软件,dw网页背景图像怎么设置?  如何高效配置IIS服务器搭建网站?  制作无缝贴图网站有哪些,3dmax无缝贴图怎么调?  香港服务器建站指南:免备案优势与SEO优化技巧全解析  建站之星在线版空间:自助建站+智能模板一键生成方案  常州企业建站如何选择最佳模板?  官网网站制作腾讯审核要多久,联想路由器newifi官网  打鱼网站制作软件,波克捕鱼官方号怎么注册?  如何通过VPS搭建网站快速盈利?  如何选择服务器才能高效搭建专属网站?  建设网站制作价格,怎样建立自己的公司网站?  大连网站制作公司哪家好一点,大连买房网站哪个好?  C#怎么创建控制台应用 C# Console App项目创建方法  合肥制作网站的公司有哪些,合肥聚美网络科技有限公司介绍?  如何在搬瓦工VPS快速搭建网站?  建站DNS解析失败?如何正确配置域名服务器?  如何用狗爹虚拟主机快速搭建网站?  建站之星如何修改网站生成路径?  为什么Go需要go mod文件_Go go mod文件作用说明  网站制作模板下载什么软件,ppt模板免费下载网站?  南阳网站制作公司推荐,小学电子版试卷去哪里找资源好?  武汉网站如何制作,黄黄高铁武穴北站途经哪些村庄?  宝塔建站无法访问?如何排查配置与端口问题?  如何在万网自助建站中设置域名及备案?  子杰智能建站系统|零代码开发与AI生成SEO优化指南  常州自助建站费用包含哪些项目?  如何高效利用200m空间完成建站?  企业宣传片制作网站有哪些,传媒公司怎么找企业宣传片项目?  如何在阿里云部署织梦网站?  家族网站制作贴纸教程视频,用豆子做粘帖画怎么制作?  建站之星ASP如何实现CMS高效搭建与安全管理?  宝塔新建站点报错如何解决?  如何在万网主机上快速搭建网站?  深圳网站制作案例,网页的相关名词有哪些?  建站之星如何保障用户数据免受黑客入侵?  建站之星安装路径如何正确选择及配置?  如何设置并定期更换建站之星安全管理员密码?  高端建站如何打造兼具美学与转化的品牌官网?  猪八戒网站制作视频,开发一个猪八戒网站,大约需要多少?或者自己请程序员,需要什么程序员,多少程序员能完成?  php8.4新语法match怎么用_php8.4match表达式替代switch【方法】  如何通过宝塔面板实现本地网站访问?  ,怎么在广州志愿者网站注册?  高性能网站服务器配置指南:安全稳定与高效建站核心方案 

您的项目需求

*请认真填写需求信息,我们会在24小时内与您取得联系。