java多线程(三)-Executors实现的几种线程池以及Callable
从java5开始,类库中引入了很多新的管理调度线程的API,最常用的就是Executor(执行器)框架。Executor帮助程序员管理Thread对象,简化了并发编程,它其实就是在 提供了一个中间层,方便程序员管理异步任务的执行,而又不用显式的管理线程的生命周期。
Executor采用了线程池实现,也更节约开销,因为是我们启动新线程的首选方法。
示例代码:src/thread_runnable/CachedThreadPool.java
public class CachedThreadPool{
public static void main(String[] args) {
ExecutorService exec = Executors.newCachedThreadPool();
System.out.println("main start " + Thread.currentThread().getName());
for (int i=0; i<3; i++){
exec.execute(new CountDown());
}
exec.shutdown();
System.out.println("main end " + Thread.currentThread().getName());
}
}
输出结果:(每次执行输出结果都有差异)

Executors类通过提供一系列的工厂方法来创建线程池,返回的线程池都实现了ExecutorService接口,ExecutorService接口实现了Executor接口,提供了更丰富的方法来管理线程池,ExecutorService对象可以通过execute(Runnable)/submit(Callable)方法来开始执行新的方法(这本身也属于命令设计模式)。
ExecutorService(其实就是线程池)的生命周期包括三种状态,运行,关闭,终止。创建后便进入运行状态。调用shutdown()方法就进入关闭状态。此时ExecutorService不再接受新的任务,但是它还在执行已经提交的任务,等到所有的任务都执行完毕后,就到了终止状态。
Executors类通过提供一系列的工厂方法来创建线程池,常见类型如下:
public static ExecutorService newFixedThreadPool(int nThreads)
创建固定数目的线程池
public static ExecutorService newCachedThreadPool()
创建一个可缓存的线程池,调用execute添加新任务后,如果有之前构造的可用线程,则重用该线程;否则,就创建一个新线程并添加到池中。并且可以从缓存中移除那些60s未被使用的线程。
public static ExecutorService newSingleThreadExecutor()
创建一个单线程的线程池。其实只有一个线程,所有的任务按照指定的顺序,(FIFO,LIFO,优先级)来执行。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
创建一个定长线程池,支持定时及周期性的任务执行,所以很多时候可用来代替Timer类。
看一个简单的例子,代码地址:src/thread_runnable/FixedThreadPool.java
public class FixedThreadPool{
public static void main(String[] args) {
int ThreadCount = 2;
ExecutorService exec = Executors.newFixedThreadPool(ThreadCount);
System.out.println("main start " + Thread.currentThread().getName());
for (int i=0; i<3; i++){
exec.execute(new CountDown());
}
exec.shutdown();
System.out.println("main end " + Thread.currentThread().getName());
}
}
当ThreadCount = 2时,某一次输出结果为:

从输出结果可以看出,在最初的时候,只有thread-1和thread-2两个线程在执行任务,当之前的任务执行完成之后,thread-2被复用,开始执行第三个任务。
那么我们可以想象,如果我们把 让ThreadCount = 1,那么应该只存在一个线程,其执行完一个任务,再接着执行另一个。
修改ThreadCount = 1,输出结果符合我们的预期:

可以周期或者delay执行的 线程也是在实际项目中也是比较常见的。
示例代码地址:src\thread_runnable\ScheduledExecutorServiceDemo.java
public class ScheduledExecutorServiceDemo {
public static void main(String[] args) {
// TODO Auto-generated method stub
ScheduledExecutorService mScheduledService = Executors.newScheduledThreadPool(2);
System.out.println("ScheduledExecutorServiceDemo --- main start, " + Thread.currentThread().getName());
mScheduledService.schedule(new CountDown(), 2 , TimeUnit.SECONDS);
System.out.println("ScheduledExecutorServiceDemo --- main end, " + Thread.currentThread().getName());
mScheduledService.shutdown();
}
}
输出结果:(通过实际输出,可以看到 ,两秒钟之后,才开始执行任务当中的打印 语句)

关于周期性任务的demo。
示例代码地址:src\thread_runnable\ScheduledFixedServiceDemo_2.java
public class ScheduledFixedServiceDemo_2 {
public static void main(String[] args) {
// TODO Auto-generated method stub
ScheduledExecutorService mScheduledService = Executors.newScheduledThreadPool(2);
System.out.println("ScheduledFixedServiceDemo_2 --- main start, " + Thread.currentThread().getName());
mScheduledService.scheduleWithFixedDelay(new Runnable() {
int count = 0;
@Override
public void run() {
System.out.println("fixed print = " + count++);
}
}, 1, 2 , TimeUnit.SECONDS);
System.out.println("ScheduledFixedServiceDemo_2 --- main end, " + Thread.currentThread().getName());
}
}
输出结果:

…..后面每间隔2s,输出一次。
Runnable虽然执行任务,但是没有返回值,而java 5之后 ,重新引入了一个 Callable接口,它是一个具有类型参数的泛型,表示从call()方法中返回的值。不过需要注意的是,把Callable对象传递给ExecutorService的submit方法后,在新线程上执行的call()方法返回的其实是 Future对象,Future对象有isDone(),get()等方法来判断是否完成,以及得到返回值等。
代码地址:src\thread_runnable\CallableDemo.java
class TaskWithResult implements Callable<String> {
public String call() throws Exception {
return "result of TaskWithResult from Callable " + "\t " + Thread.currentThread().getName();
}
}
public class CallableDemo {
public static void main(String[] args) {
// TODO Auto-generated method stub
ExecutorService exec = Executors.newCachedThreadPool();
System.out.println("main start\t" + Thread.currentThread().getName());
Future<String> fs = exec.submit(new TaskWithResult());
exec.shutdown();
try {
//当callable对象并没有完成时,这里一直是堵塞的。
System.out.println(fs.get());
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("main end\t" + Thread.currentThread().getName());
}
}
输出结果:

这几篇java多线程文章的demo代码下载地址 http://download.csdn.net/detail/yaowen369/9786452
-------
作者: www.yaoxiaowen.com
github: https://github.com/yaowen369
java多线程(三)-Executors实现的几种线程池以及Callable的更多相关文章
- JAVA基础知识|Executors提供的四种线程池
一.Thread与Executors 开启新的线程,我们经常会采用如下方法: Thread thread =new Thread(new Runnable() { @Override public v ...
- Java第三阶段学习(七、线程池、多线程)
一.线程池 1.概念: 线程池,其实就是一个容纳多个线程的容器,其中的线程可以重复使用,省去了频繁创建线程对象的过程,无需反复创建线程而消耗过多资源,是JDK1.5以后出现的. 2.使用线程池的方式- ...
- JAVA多线程-内存模型、三大特性、线程池
一.线程的三大特性 原子性.可见性.有序性 1)原子性,即一个操作或者多个操作要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行.原子性其实就是保证数据一致.线程安全一部分. 2)可见性,即 ...
- Executors创建的4种线程池的使用
Java通过Executors提供四种线程池,分别为:newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程.newFixe ...
- Executors提供的四种线程池和自定义线程池
JAVA并发编程——EXECUTORS 线程池的思想是一种对象池的思想,开放一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理.当有线程任务时,从池中取一个,执行完毕,对象 ...
- 多线程----Thread类,Runnable接口,线程池,Callable接口,线程安全
1概念 1.1进程 进程指正在运行的程序.确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能. 任务管理器中: 1.2线程 线程是进程中的一个执行单元 ...
- Java多线程总结(二)锁、线程池
掌握Java中的多线程,必须掌握Java中的各种锁,以及了解Java中线程池的运用.关于Java多线程基础总结可以参考我的这篇博文Java多线程总结(一)多线程基础 转载请注明出处——http://w ...
- Executors提供的四种线程池
Java 5+中的Executor接口定义一个执行线程的工具.它的子类型即线程池接口是ExecutorService.要配置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,因此在工具 ...
- JDK提供的四种线程池代码详解
一.线程池什么时候使用,会给我们带来什么好处? 如果很多用户去访问服务器,用户访问服务器的时间是非常短暂的,那么有可能在创建线程和销毁线程上花费的时间会远远大于访问所消耗的时间,如果采用线程池会使线程 ...
随机推荐
- PHPstorm 函数时间注释的修改
正常的PHPstorm里面函数方法的注释是没有动态时间设置的,但是看了PHP file里面有时间日期的注释,而PHP Function Doc Comment 却没有,让很多PHPer很头疼,今天在搜 ...
- 对于vue.js初步了解
由于本人做的是javaWeb的开发,对于前端的了解还是有限,今天对于vue.js了解了下(主要是看官方api),把自己的心得说一下,希望各位大神可以补充,谢谢 http://www.runoob. ...
- 查看主机DNSserver
一.Nslookup(name server lookup)( 域名查询):是一个用于查询 Internet 域名信息或诊断DNS server问题的工具.nslookup能够指定查询的类型,能够查到 ...
- MySQL数据库使某个不是主键的字段唯一
在使用MySQL数据的过程中有时候我们须要某个不是主键的字段不反复.这个时候就要用到SQL的UNIQUE约束了. 以下摘抄自w3school里的一段介绍: UNIQUE 约束唯一标识数据库表中的每条记 ...
- LINUX6安装Oracle10g无法启动安装界面解决
***********************************************声明*************************************************** ...
- nginx、fastCGI、php-fpm关系梳理
前言: Linux下搭建nginx+php+memached(LPMN)的时候,nginx.conf中配需要配置fastCGI,php需要安装php-fpm扩展并启动php-fpm守护进程,nginx ...
- flask 上传文件
flask upload 近日在学习python,接触到了flask框架,刚好客户有个需求,需要在网页上传一个python 代码的zip包,然后使用docker 容器运行这个zip里面的程序,输出结果 ...
- (转)Maven依赖的jar包下载不了、jar更新不了的解决办法
场景一: 使用Maven的同学可能偶尔会遇到这种情况:pom.xml中依赖了项目需要的某个jar文件,但是使用Maven –> update project 还是没办法下载该jar到项目中,你可 ...
- HTTPS加密流程超详解(一)前期准备
0.前言 前一阵子想写一个HTTPS的嗅探工具,之前只是大致了解SSL/TLS协议的加密流程,真正上起手来一步一步分析发现还是有点复杂的,于是我参考了wireshark的源码以及各种RFC,弄清楚了S ...
- springboot整合redis
springboot-整合redis springboot学习笔记-4 整合Druid数据源和使用@Cache简化redis配置 一.整合Druid数据源 Druid是一个关系型数据库连接池,是阿 ...