线程池
多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力。
假设一个服务器完成一项任务所需时间为:T1 创建线程时间,T2 在线程中执行任务的时间,T3 销毁线程时间。
如果:T1 + T3 远大于 T2,则可以采用线程池,以提高服务器性能。
一个线程池包括以下四个基本组成部分:
1. 线程池管理器(ThreadPool):用于创建并管理线程池,包括 创建线程池,销毁线程池,添加新任务;
2. 工作线程(PoolWorker):线程池中线程,在没有任务时处于等待状态,可以循环的执行任务;
3. 任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等;
4. 任务队列(taskQueue):用于存放没有处理的任务。提供一种缓冲机制。
线程池技术正是关注如何缩短或调整T1,T3时间的技术,从而提高服务器程序性能的。它把T1,T3分别安排在服务器程序的启动和结束的时间段或者一些空闲的时间段,这样在服务器程序处理客户请求时,不会有T1,T3的开销了。
线程池不仅调整T1,T3产生的时间段,而且它还显著减少了创建线程的数目,看一个例子:
假设一个服务器一天要处理50000个请求,并且每个请求需要一个单独的线程完成。在线程池中,线程数一般是固定的,所以产生线程总数不会超过线程池中线程的数目,而如果服务器不利用线程池来处理这些请求则线程总数为50000。一般线程池大小是远小于50000。所以利用线程池的服务器程序不会为了创建50000而在处理请求时浪费时间,从而提高效率。
线程池的作用
线程池作用就是限制系统中执行线程的数量。
根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果;少了浪费了系统资源,多了造成系统拥挤效率不高。用线程池控制线程数量,其他线程 排队等候。一个任务执行完毕,再从队列的中取最前面的任务开始执行。若队列中没有等待进程,线程池的这一资源处于等待。当一个新任务需要运行时,如果线程池中有等待的工作线程,就可以开始运行了;否则进入等待队列。
为什么使用线程池
- 减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务
- 可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)
Java中线程池的管理
Java API中对线程池的管理主要包含三个类:
1. Executor接口表示线程池,它的executr(Runnable task)方法用来执行Runnable类型的任务
2. ExecitorService是Executor的子接口,它声明了管理线程池的一些方法,比如用于关闭线程池的shutdowun()方法等
3. Executors类中包含一些静态方法,类似于工具类,负责生成各种类型的线程池ExecutorService实例
创建不同的线程池
首先声明一个任务,使用它来演示不同的线程池的使用,输出结果自行验证
public class WorkerThread implements Runnable {
private String command;
public WorkerThread(String s) {
this.command = s;
}
public void run() {
System.out.println(Thread.currentThread().getName() + " Start. Time = " + new Date());
processCommand();
System.out.println(Thread.currentThread().getName() + " End. Time = " + new Date());
}
private void processCommand() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public String toString() {
return this.command;
}
}
CachedThreadPool
CachedThreadPool会创建一个缓存区,将初始化的线程缓存起来。会终止并且从缓存中移除已有60秒未被使用的线程。如果线程有可用的,就使用之前创建好的线程,如果线程没有可用的,就新创建线程。
public class CachedThreadPoolDemo {
public static void main(String[] args) throws InterruptedException {
ExecutorService service = Executors.newCachedThreadPool();
int size = new Random().nextInt(10);// 随机生成若干个线程
for (int i = 0; i < size; i++) {
service.execute(new WorkerThread("线程" + i));
}
TimeUnit.SECONDS.sleep(5);
service.shutdown();
}
}
Executors.newCachedThreadPool()创建的线程池最大的特点就是线程数量可变。
FixedThreadPool
在FixedThreadPool中,有一个固定大小的池。如果当前需要执行的任务超过池大小,那么多出的任务处于等待状态,直到有空闲下来的线程执行任务,如果当前需要执行的任务小于池大小,空闲的线程也不会去销毁
public class FixedThreadPoolDemo {
public static void main(String[] args) throws InterruptedException {
int size = new Random().nextInt(10);// 随机生成若干个线程
ExecutorService service = Executors.newFixedThreadPool(size);
for (int i = 0; i < size; i++) {
service.execute(new WorkerThread("线程" + i));
}
TimeUnit.SECONDS.sleep(5);
service.shutdown();
}
}
SingleThreadExecutor
SingleThreadExecutor 就像是线程数量为1的FixedThreadPool (指定数量的线程池)。如果向SingleThreadExecutor 提交了多个任务, 那么这些任务将排队,每个任务都会在下一任务开始前,结束运行. 所有的任务将使用相同的线程。
public class SingleThreadExecutorDemo {
public static void main(String[] args) throws InterruptedException {
ExecutorService service = Executors.newSingleThreadExecutor();
service.execute(new WorkerThread("线程"));
TimeUnit.SECONDS.sleep(5);
service.shutdown();
}
}
ScheduledThreadPool
ScheduledThreadPool可以使任务延时相应的时间再启动线程去执行
public class SingleThreadExecutorDemo {
public static void main(String[] args) throws InterruptedException {
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
System.out.println("Current Time = " + new Date());
for (int i = 0; i < 3; i++) {
Thread.sleep(1000);
WorkerThread worker = new WorkerThread("do heavy processing");
scheduledThreadPool.schedule(worker, 10, TimeUnit.SECONDS);
}
Thread.sleep(30000);
scheduledThreadPool.shutdown();
while (!scheduledThreadPool.isTerminated()) {
}
System.out.println("Finished all threads");
}
}
参考:
http://zy116494718.iteye.com/blog/1704344
http://www.importnew.com/7276.html