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线程池的分析和使用详解
# 时间为
# 操作系统
# 单线程
# 的是
# 都是
# 实现了
# 无界
# 创建一个
# 抛出
# 返回值
# 主要是
# 是一个
# 法会
# 我是
# 是在
# 会有
# 还可以
# 还在
# 多个
# 如有
相关文章:
北京网站制作费用多少,建立一个公司网站的费用.有哪些部分,分别要多少钱?
如何通过虚拟主机快速完成网站搭建?
专业型网站制作公司有哪些,我设计专业的,谁给推荐几个设计师兼职类的网站?
如何在阿里云香港服务器快速搭建网站?
韩国代理服务器如何选?解析IP设置技巧与跨境访问优化指南
上海网站制作网站建设公司,建筑电工证网上查询系统入口?
建站为何优先选择香港服务器?
建站之星安装后如何配置SEO及设计样式?
如何通过老薛主机一键快速建站?
上海制作企业网站有哪些,上海有哪些网站可以让企业免费发布招聘信息?
网站设计制作公司地址,网站建设比较好的公司都有哪些?
山东网站制作公司有哪些,山东大源集团官网?
如何访问已购建站主机并解决登录问题?
西安大型网站制作公司,西安招聘网站最好的是哪个?
太原网站制作公司有哪些,网约车营运证查询官网?
合肥做个网站多少钱,合肥本地有没有比较靠谱的交友平台?
如何自定义建站之星模板颜色并下载新样式?
深圳网站制作的公司有哪些,dido官方网站?
邀请函制作网站有哪些,有没有做年会邀请函的网站啊?在线制作,模板很多的那种?
如何快速搭建响应式可视化网站?
高端网站建设与定制开发一站式解决方案 中企动力
网站制作软件有哪些,制图软件有哪些?
宝塔Windows建站如何避免显示默认IIS页面?
浙江网站制作公司有哪些,浙江栢塑信息技术有限公司定制网站做的怎么样?
魔毅自助建站系统:模板定制与SEO优化一键生成指南
中山网站推广排名,中山信息港登录入口?
北京营销型网站制作公司,可以用python做一个营销推广网站吗?
如何快速重置建站主机并恢复默认配置?
如何用y主机助手快速搭建网站?
网站制作外包价格怎么算,招聘网站上写的“外包”是什么意思?
教育培训网站制作流程,请问edu教育网站的域名怎么申请?
rsync同步时出现rsync: failed to set times on “xxxx”: Operation not permitted
如何用AWS免费套餐快速搭建高效网站?
如何在西部数码注册域名并快速搭建网站?
javascript中的try catch异常捕获机制用法分析
如何注册花生壳免费域名并搭建个人网站?
如何在七牛云存储上搭建网站并设置自定义域名?
建站之星各版本价格是多少?
c++23 std::expected怎么用 c++优雅处理函数错误返回【详解】
宠物网站制作html代码,有没有专门介绍宠物如何养的网站啊?
网站制作说明怎么写,简述网页设计的流程并说明原因?
c++怎么编写动态链接库dll_c++ __declspec(dllexport)导出与调用【方法】
如何在阿里云服务器自主搭建网站?
小型网站制作HTML,*游戏网站怎么搭建?
网站专业制作公司,网站编辑是做什么的?好做吗?工作前景如何?
建站之星安装失败:服务器环境不兼容?
如何在建站之星网店版论坛获取技术支持?
高防服务器如何保障网站安全无虞?
建站主机服务器选购指南:轻量应用与VPS配置解析
正规网站制作公司有哪些,目前国内哪家网页网站制作设计公司比较专业靠谱?口碑好?
*请认真填写需求信息,我们会在24小时内与您取得联系。