Java多线程之使用执行器(Executors)(Thinking in Java)
2017-07-05 15:07
309 查看
JavaSE5的java.util.concurrent包中的执行器(Executor)将为你管理Thread对象,从而简化了并发编程。Executor在客户端和任务执行之间提供了一个间接层;与客户端直接执行任务不同,这个中介对象将执行任务。Executor允许你管理异步任务的执行,而无须显式地管理线程的生命周期。Executor在JavaSe5/6中是启动任务的优选方法。
如果程序中创建了大量的生命周期很短的线程,应该使用线程池。一个线程池中包含许多准备运行的空闲线程。将Runnable对象交给线程池,就会有一个线程调用run()方法。当run()方法退出时,线程不会死亡,而是在池中准备为下一个请求提供服务。
另一个使用线程池的理由是减少并发线程的数目。创建大量线程会大大降低性能甚至使虚拟机崩溃。如果有一个会创建许多多线程的算法,应该使用一个线程数“固定的”线程池以限制并发线程的总数。
执行器(Executors)类有许多静态工厂方法用来构建线程池,下面的表对这些方法进行了汇总:
我们可以使用Executor来代替在MoreBasicThreads.java中显示地创建Thread对象,LiftOff对象知道如何运行具体的任务,与命令设计模式一样,它暴露了要执行的单一方法。ExecutorService(具有服务生命周期的Executor,例如关闭)知道如何构建恰当的上下文来执行Runnable对象。在下面的示例中,CachedThreadPool将为每个任务都创建一个线程。注意,ExecutorService对象是使用静态的Executor方法创建的,这个方法可以确定其Executor类型。
测试CachedThreadPool:
package chapter21;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 测试CachedThreadPool
*/
public class CachedThreadPool {
public static void main(String[] args) {
//根据需要创建线程
ExecutorService exec = Executors.newCachedThreadPool();
for(int i =0; i < 5; i ++) {
运行结果:
#0(9), #3(9), #2(9), #1(9), #4(9), #3(8), #0(8), #4(8), #3(7), #0(7), #4(7), #1(8), #2(8), #1(7), #2(7), #1(6), #2(6), #3(6), #1(5), #0(6), #2(5), #0(5), #3(5), #4(6), #2(4), #1(4), #0(4), #2(3), #3(4), #0(3), #3(3), #0(2), #3(2), #0(1), #3(1), #0(LiftOff!),
#3(LiftOff!), #1(3), #4(5), #2(2), #1(2), #1(1), #4(4), #2(1), #1(LiftOff!), #4(3), #4(2), #4(1), #2(LiftOff!), #4(LiftOff!),
测试FixedThreadPool:
package chapter21;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 测试FixedThreadPool
*/
public class FixedThreadPoolTest {
public static void main(String[] args) {
//创建一个有5个线程的线程池
ExecutorService exec = Executors.newFixedThreadPool(5);
for(int i = 0; i < 5; i ++) {
exec.execute(new LiftOff());
}
//当所有的任务执行完毕后,关闭线程池
exec.shutdown();
}
}
执行结果:
#0(9), #0(8), #0(7), #0(6), #0(5), #0(4), #0(3), #0(2), #0(1), #0(LiftOff!), #1(9), #1(8), #1(7), #1(6), #1(5), #1(4), #1(3), #1(2), #1(1), #1(LiftOff!), #2(9), #2(8), #2(7), #2(6), #2(5), #2(4), #2(3), #2(2), #2(1), #2(LiftOff!), #3(9), #3(8), #3(7), #3(6),
#3(5), #3(4), #3(3), #3(2), #3(1), #3(LiftOff!), #4(9), #4(8), #4(7), #4(6), #4(5), #4(4), #4(3), #4(2), #4(1), #4(LiftOff!),
SingleThreadExecutor就像是线程数量为1的FixedThreadPool。这对于你希望在另一个线程里持续运行的任何事物(长期存活的任务)来说,都是很有用的,例如监听进入的套接字连接的任务,它对于希望在线程中运行的短任务也同样很方便,例如,更新本地或远程日志的小任务或者事件分发线程。
如果向SingleThreadExecutor提交了多个任务,那么这些任务将排队,每个任务都会在下一个任务开始之前运行结束,所有的任务将使用相同的线程。在下面的示例中,你可以看到每个任务都是按照它们被提交的顺序,并且是在下一个任务开始之前完成的。因此,SingleThreadExecutor会序列化所有交给它的任务,并会维护它自己(隐藏)的悬挂任务队列。
下面演示SingleThreadPool:
package chapter21;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 测试SingleThreadExecutor
*/
public class SingleThreadExecutorTest {
public static void main(String[] args) {
ExecutorService exec = Executors.newSingleThreadExecutor();
for(int i = 0; i < 5; i ++) {
exec.execute(new LiftOff());
}
//当所有任务执行完毕后,关闭线程池
exec.shutdown();
}
}
运行结果:
#0(9), #0(8), #0(7), #0(6), #0(5), #0(4), #0(3), #0(2), #0(1), #0(LiftOff!), #1(9), #1(8), #1(7), #1(6), #1(5), #1(4), #1(3), #1(2), #1(1), #1(LiftOff!), #2(9), #2(8), #2(7), #2(6), #2(5), #2(4), #2(3), #2(2), #2(1), #2(LiftOff!), #3(9), #3(8), #3(7), #3(6),
#3(5), #3(4), #3(3), #3(2), #3(1), #3(LiftOff!), #4(9), #4(8), #4(7), #4(6), #4(5), #4(4), #4(3), #4(2), #4(1), #4(LiftOff!),
如果程序中创建了大量的生命周期很短的线程,应该使用线程池。一个线程池中包含许多准备运行的空闲线程。将Runnable对象交给线程池,就会有一个线程调用run()方法。当run()方法退出时,线程不会死亡,而是在池中准备为下一个请求提供服务。
另一个使用线程池的理由是减少并发线程的数目。创建大量线程会大大降低性能甚至使虚拟机崩溃。如果有一个会创建许多多线程的算法,应该使用一个线程数“固定的”线程池以限制并发线程的总数。
执行器(Executors)类有许多静态工厂方法用来构建线程池,下面的表对这些方法进行了汇总:
方法 | 描述 |
newFixedThreadPool | 该池包含固定数量的线程,空闲线程会被一直保留 |
newCachedThreadPool | 必要时创建新线程,空闲线程会被保留60秒 |
newSingleThreadPool | 只有一个线程的线程池,该线程顺序执行每一个提交的任务(类似于Swing事件分配线程) |
newScheduledThreadPool | 用于预定执行而构建的固定线程池,替代java.util.Timer |
newSingleThreadScheduledPool | 用于预定执行而构建的单线程池 |
测试CachedThreadPool:
package chapter21;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 测试CachedThreadPool
*/
public class CachedThreadPool {
public static void main(String[] args) {
//根据需要创建线程
ExecutorService exec = Executors.newCachedThreadPool();
for(int i =0; i < 5; i ++) {
//LiftOff参见前一篇博客exec.execute(new LiftOff());//将任务放入线程池中执行}//当所有线程执行完成后,关闭线程池exec.shutdown();}}
运行结果:
#0(9), #3(9), #2(9), #1(9), #4(9), #3(8), #0(8), #4(8), #3(7), #0(7), #4(7), #1(8), #2(8), #1(7), #2(7), #1(6), #2(6), #3(6), #1(5), #0(6), #2(5), #0(5), #3(5), #4(6), #2(4), #1(4), #0(4), #2(3), #3(4), #0(3), #3(3), #0(2), #3(2), #0(1), #3(1), #0(LiftOff!),
#3(LiftOff!), #1(3), #4(5), #2(2), #1(2), #1(1), #4(4), #2(1), #1(LiftOff!), #4(3), #4(2), #4(1), #2(LiftOff!), #4(LiftOff!),
测试FixedThreadPool:
package chapter21;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 测试FixedThreadPool
*/
public class FixedThreadPoolTest {
public static void main(String[] args) {
//创建一个有5个线程的线程池
ExecutorService exec = Executors.newFixedThreadPool(5);
for(int i = 0; i < 5; i ++) {
exec.execute(new LiftOff());
}
//当所有的任务执行完毕后,关闭线程池
exec.shutdown();
}
}
执行结果:
#0(9), #0(8), #0(7), #0(6), #0(5), #0(4), #0(3), #0(2), #0(1), #0(LiftOff!), #1(9), #1(8), #1(7), #1(6), #1(5), #1(4), #1(3), #1(2), #1(1), #1(LiftOff!), #2(9), #2(8), #2(7), #2(6), #2(5), #2(4), #2(3), #2(2), #2(1), #2(LiftOff!), #3(9), #3(8), #3(7), #3(6),
#3(5), #3(4), #3(3), #3(2), #3(1), #3(LiftOff!), #4(9), #4(8), #4(7), #4(6), #4(5), #4(4), #4(3), #4(2), #4(1), #4(LiftOff!),
SingleThreadExecutor就像是线程数量为1的FixedThreadPool。这对于你希望在另一个线程里持续运行的任何事物(长期存活的任务)来说,都是很有用的,例如监听进入的套接字连接的任务,它对于希望在线程中运行的短任务也同样很方便,例如,更新本地或远程日志的小任务或者事件分发线程。
如果向SingleThreadExecutor提交了多个任务,那么这些任务将排队,每个任务都会在下一个任务开始之前运行结束,所有的任务将使用相同的线程。在下面的示例中,你可以看到每个任务都是按照它们被提交的顺序,并且是在下一个任务开始之前完成的。因此,SingleThreadExecutor会序列化所有交给它的任务,并会维护它自己(隐藏)的悬挂任务队列。
下面演示SingleThreadPool:
package chapter21;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 测试SingleThreadExecutor
*/
public class SingleThreadExecutorTest {
public static void main(String[] args) {
ExecutorService exec = Executors.newSingleThreadExecutor();
for(int i = 0; i < 5; i ++) {
exec.execute(new LiftOff());
}
//当所有任务执行完毕后,关闭线程池
exec.shutdown();
}
}
运行结果:
#0(9), #0(8), #0(7), #0(6), #0(5), #0(4), #0(3), #0(2), #0(1), #0(LiftOff!), #1(9), #1(8), #1(7), #1(6), #1(5), #1(4), #1(3), #1(2), #1(1), #1(LiftOff!), #2(9), #2(8), #2(7), #2(6), #2(5), #2(4), #2(3), #2(2), #2(1), #2(LiftOff!), #3(9), #3(8), #3(7), #3(6),
#3(5), #3(4), #3(3), #3(2), #3(1), #3(LiftOff!), #4(9), #4(8), #4(7), #4(6), #4(5), #4(4), #4(3), #4(2), #4(1), #4(LiftOff!),
相关文章推荐
- java创建多线程使用Executors创造ExecutorService
- 多线程Java:使用Executors创建和管理线程
- java创建多线程使用Executors创造ExecutorService
- java创建多线程使用Executors创造ExecutorService
- java多线程之Condition的使用
- Java 多线程编程之三:synchronized 关键字的使用
- Java:使用Executors创建和管理线程
- jdk1.4 构建 java多线程,并发设计框架 使用列子(一)
- thinking in java 附录A 使用非JAVA代码
- 6. 初学Java多线程:慎重使用volatile关键字
- java多线程如何使用
- 如何使用Java编写多线程程序
- JAVA实现多线程的两种方法,及如何使用
- Java多线程基础使用(Thread|Runable|TimerTask&Timer)
- Java:使用Executors创建和管理线程
- 使用JAVA建立稳定的多线程服务器
- Java中使用多线程复制文件
- 不可以使用 Firefox 2.0 调试 Java HttpSession 多线程访问
- jdk1.4 构建 java多线程,并发设计框架 使用列子(二)
- java多线程设计wait、notify、notifyall、synchronized的使用机制