从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的更多相关文章

  1. JAVA基础知识|Executors提供的四种线程池

    一.Thread与Executors 开启新的线程,我们经常会采用如下方法: Thread thread =new Thread(new Runnable() { @Override public v ...

  2. Java第三阶段学习(七、线程池、多线程)

    一.线程池 1.概念: 线程池,其实就是一个容纳多个线程的容器,其中的线程可以重复使用,省去了频繁创建线程对象的过程,无需反复创建线程而消耗过多资源,是JDK1.5以后出现的. 2.使用线程池的方式- ...

  3. JAVA多线程-内存模型、三大特性、线程池

    一.线程的三大特性 原子性.可见性.有序性 1)原子性,即一个操作或者多个操作要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行.原子性其实就是保证数据一致.线程安全一部分. 2)可见性,即 ...

  4. Executors创建的4种线程池的使用

    Java通过Executors提供四种线程池,分别为:newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程.newFixe ...

  5. Executors提供的四种线程池和自定义线程池

    JAVA并发编程——EXECUTORS 线程池的思想是一种对象池的思想,开放一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理.当有线程任务时,从池中取一个,执行完毕,对象 ...

  6. 多线程----Thread类,Runnable接口,线程池,Callable接口,线程安全

    1概念 1.1进程 进程指正在运行的程序.确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能. 任务管理器中: 1.2线程 线程是进程中的一个执行单元 ...

  7. Java多线程总结(二)锁、线程池

    掌握Java中的多线程,必须掌握Java中的各种锁,以及了解Java中线程池的运用.关于Java多线程基础总结可以参考我的这篇博文Java多线程总结(一)多线程基础 转载请注明出处——http://w ...

  8. Executors提供的四种线程池

    Java 5+中的Executor接口定义一个执行线程的工具.它的子类型即线程池接口是ExecutorService.要配置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,因此在工具 ...

  9. JDK提供的四种线程池代码详解

    一.线程池什么时候使用,会给我们带来什么好处? 如果很多用户去访问服务器,用户访问服务器的时间是非常短暂的,那么有可能在创建线程和销毁线程上花费的时间会远远大于访问所消耗的时间,如果采用线程池会使线程 ...

随机推荐

  1. angularjs 给封装的模态框元素传值,和实现兄弟传值

    本例实现封装的元素所放的位置不同,而选择不同的传值,这里举例封装了bootstrap模态框,以后也方便大家去直接使用.方法举例如下:首先主页调用css/js有: <link rel=" ...

  2. TensorBoard 使用和问题解决

    TensorBoard 使用和问题解决 一.启动TensorBoard 1) python tensorflow/tensorboard/tensorboard.py --logdir=path/to ...

  3. MySQL系列:高可用架构之MHA

    前言 从11年毕业到现在,工作也好些年头,入坑mysql也有近四年的时间,也捣鼓过像mongodb.redis.cassandra.neo4j等Nosql数据库.其实一直想写博客分享下工作上的零零碎碎 ...

  4. ArrayList与数组间的转换

    关键句:String[] array = (String[])list.toArray(new String[size]); public class Test { public static voi ...

  5. 我的Android进阶之旅------&gt;Android知识图谱

    Android知识图谱.快来看看哪方面有漏洞? 该图转自:http://blog.csdn.net/xyz_lmn/article/details/41411355

  6. 一道题Wrong Answer之后该何去何从?

    写程序手不稳是个大毛病,往往会让一份能AC的代码变成99.995%正确,失之毫厘谬以千里,近期十场个人赛非常少有能一次AC的经历,细致想想除了根本逻辑上的错误.大概都是跪在这些细节上: 1.输出格式, ...

  7. JS和JQuery中的事件托付 学习笔记

    事件托付事实上并非一个非常高级的技巧,比方在一个页面里面.当仅仅存在两个button的时候.可能你给button加入监听是这种:(本文不考虑浏览器兼容性.关于事件的兼容性可參考前面的学习笔记) < ...

  8. C# 函数式编程 —— 使用 Lambda 表达式编写递归函数

    最近看了赵姐夫的这篇博客http://blog.zhaojie.me/2009/08/recursive-lambda-expressions.html,主要讲的是如何使用 Lambda 编写递归函数 ...

  9. FiddlerCoreAPI开发(二)截获HTTPS流量

    上一篇文章简单简单分析了fiddlercore自带样例的代码,本篇文章进入主题,介绍如何使用fiddlercore截获HTTPS流量. 当时学习完样例代码后,我觉得结合注释来抓HTTPS的包应该也很简 ...

  10. dotnetcore 自动迁移工具

    费心思做了一个简单的dotnetcore迁移工具,欢迎大家使用和交流 工具所做的工作: 查找所有输入目录的子目录和上级目录,获取包含*.sln的项目集合,可批量迁移. 替换*.sln文件中的*.csp ...