从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. 数据库索引------Hash索引的使用限制

    1.hash索引必须进行二次查找. 2.hash索引无法进行排序. 3.hash索引不支持部分索引查找也不支持范围查找. 4.hash索引中hash码的计算可能存在hash冲突.

  2. 基本MarkDown语法

    引言 字符串作为一种常见的数据类型,在日常中我们面临各式各样的字符串处理问题,那么,这就要求我们必须掌握一些常用的字符串处理函数.本文尽量囊括常用的字符串处理函数,此文也作为个人的小总结. Pytho ...

  3. HTTP/2之服务器推送(Server Push)最佳实践

    商业转载请联系腾讯WeTest获得授权,非商业转载请注明出处.   WeTest 导读 HTTP/1.X出色地满足互联网的普遍访问需求,但随着互联网的不断发展,其性能越来越成为瓶颈.IETF在2015 ...

  4. while(true)应用 之 实现自己的消息队列

    早些时候,一直有个疑问,就是比如你从前端发一个操作之后,后台为什么能够及时处理你的东西呢?当然了,我说的不是,服务器为什么能够立即接收到你的请求之类高大上的东西.而是,假设你用异步去做一个事情,而后台 ...

  5. Linux 账号管理与 ACL 权限配置

    要登陆 Linux 系统一定要有账号与口令才行,否则怎么登陆,您说是吧?不过, 不同的使用者应该要拥有不同的权限才行吧?我们还可以透过 user/group 的特殊权限配置, 来规范出不同的群组开发项 ...

  6. 翻译:SET NAMES

    */ .hljs { display: block; overflow-x: auto; padding: 0.5em; color: #333; background: #f8f8f8; } .hl ...

  7. 快看Sample代码,速学Swift语言(2)-基础介绍

    Swift语言是一个新的编程语言,用于iOS, macOS, watchOS, 和 tvOS的开发,不过Swift很多部分内容,我们可以从C或者Objective-C的开发经验获得一种熟悉感.Swif ...

  8. android 网络请求Volley的简单使用

    下载到本地jar包或者在线导入,jar地址:链接:http://pan.baidu.com/s/1gf3VZAb 密码:mmye //定义变量 private RequestQueue mQueue= ...

  9. 链接文本在a标签内标签里也可以用driver.find_element_by_link_text

    如: 也可用driver.find_element_by_link_text("账户登录").click()

  10. Delphi中的RectTracker - 原创

    本文算是副产品,正品是利用FFmpeg从任意视频中生成GIF片段的小程序,写完了就发. 因为要对视频画面进行框选,再生成GIF,所以得有个框选的控件,可Delphi里没有啊,只好自己写一个了. 声明 ...