java并发——线程池

1 线程池优点

合理利用线程池能够带来三个好处:
第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
 

2 线程池的继承架构

程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。
线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。

Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。
真正的线程池接口是ExecutorService。下面这张图完整描述了线程池的类体系结构。

Executor是一个顶层接口,在它里面只声明了一个方法execute(Runnable),返回值为void,参数为Runnable类型,从字面意思可以理解,就是用来执行传进去的任务的;

然后ExecutorService接口继承了Executor接口,并声明了一些方法:submit、invokeAll、invokeAny以及shutDown等;
抽象类AbstractExecutorService实现了ExecutorService接口,基本实现了ExecutorService中声明的所有方法;
然后ThreadPoolExecutor继承了类AbstractExecutorService。

3 常用线程池

要配置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,很有可能配置的线程池不是较优的,
因此在Executors类里面提供了一些静态工厂,生成一些常用的线程池。
 
newSingleThreadExecutor:创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
newFixedThreadPool:创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。 线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
newCachedThreadPool:创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。 此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。
newScheduledThreadPool:创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。
newSingleThreadExecutor:创建一个单线程的线程池。此线程池支持定时以及周期性执行任务的需求。
 
这些方法的返回值是ExecutorService对象,该对象表示一个线程池,可以执行Runnable对象或者Callable对象代表的线程。
它提供了如下方法来提交一个任务:
Future<?> submit(Runnable task)
<T> Future<T> submit(Callable<T> task)

4、使用线程池实例一

package com.study.thread;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

class MyTask implements Runnable{
	private int tasknum;
	
	public MyTask(int num){
		this.tasknum=num;
	}
	@Override
	public void run(){
		System.out.println("正在执行task"+tasknum);
		try{
			Thread.currentThread().sleep(4000);
		}catch(InterruptedException e){
			e.printStackTrace();
		}
		System.out.println("task "+tasknum+"执行完毕");
	}
}
public class ThreadPoolTest1 {
	
	public static void main(String[] args){
		
		ThreadPoolExecutor executor=new ThreadPoolExecutor(5,10,200,
				TimeUnit.MICROSECONDS,new ArrayBlockingQueue<Runnable>(5));
		
		for(int i=0;i<15;i++){
			MyTask myTask=new MyTask(i);
			executor.execute(myTask);
			
			System.out.println("线程池中线程数量:"+executor.getPoolSize()+
					"队列中等待的任务数:"+executor.getQueue().size()+"已执行完的任务数目:"+executor.getCompletedTaskCount());
		}
	}
}
运行结果:
线程池中线程数量:1队列中等待的任务数:0已执行完的任务数目:0
线程池中线程数量:2队列中等待的任务数:0已执行完的任务数目:0
线程池中线程数量:3队列中等待的任务数:0已执行完的任务数目:0
线程池中线程数量:4队列中等待的任务数:0已执行完的任务数目:0
线程池中线程数量:5队列中等待的任务数:0已执行完的任务数目:0
线程池中线程数量:5队列中等待的任务数:1已执行完的任务数目:0
线程池中线程数量:5队列中等待的任务数:2已执行完的任务数目:0
线程池中线程数量:5队列中等待的任务数:3已执行完的任务数目:0
线程池中线程数量:5队列中等待的任务数:4已执行完的任务数目:0
线程池中线程数量:5队列中等待的任务数:5已执行完的任务数目:0
线程池中线程数量:6队列中等待的任务数:5已执行完的任务数目:0
线程池中线程数量:7队列中等待的任务数:5已执行完的任务数目:0
线程池中线程数量:8队列中等待的任务数:5已执行完的任务数目:0
线程池中线程数量:9队列中等待的任务数:5已执行完的任务数目:0
线程池中线程数量:10队列中等待的任务数:5已执行完的任务数目:0
正在执行task1
正在执行task10
正在执行task14
正在执行task3
正在执行task2
正在执行task12
正在执行task11
正在执行task0
正在执行task4
正在执行task13
task 14执行完毕
正在执行task5
task 10执行完毕
正在执行task6
task 1执行完毕
正在执行task7
task 2执行完毕
正在执行task8
task 3执行完毕
正在执行task9
task 11执行完毕
task 12执行完毕
task 0执行完毕
task 13执行完毕
task 4执行完毕
task 7执行完毕
task 6执行完毕
task 5执行完毕
task 9执行完毕
task 8执行完毕
从执行结果可以看出,当线程池中线程的数目大于5时,便将任务放入任务缓存队列里面,当任务缓存队列满了之后,便创建新的线程。
如果上面程序中,将for循环中改成执行20个任务,就会抛出任务拒绝异常了。

不过在java doc中,并不提倡我们直接使用ThreadPoolExecutor,而是使用Executors类中提供的几个静态方法来创建线程池:
Executors.newCachedThreadPool();        //创建一个缓冲池,缓冲池容量大小为Integer.MAX_VALUE
Executors.newSingleThreadExecutor();    //创建容量为1的缓冲池
Executors.newFixedThreadPool(int);      //创建固定容量大小的缓冲池
下面是这三个静态方法的具体实现:
public static ExecutorService newFixedThreadPool(int nThreads) {
     return new ThreadPoolExecutor(nThreads, nThreads,
                                   0L, TimeUnit.MILLISECONDS,
                                   new LinkedBlockingQueue<Runnable>());
 }
 public static ExecutorService newSingleThreadExecutor() {
     return new FinalizableDelegatedExecutorService
         (new ThreadPoolExecutor(1, 1,
                                 0L, TimeUnit.MILLISECONDS,
                                 new LinkedBlockingQueue<Runnable>()));
 }
 public static ExecutorService newCachedThreadPool() {
     return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                   60L, TimeUnit.SECONDS,
                                   new SynchronousQueue<Runnable>());
 }

从它们的具体实现来看,它们实际上也是调用了ThreadPoolExecutor,只不过参数都已配置好了。
  newFixedThreadPool创建的线程池corePoolSize和maximumPoolSize值是相等的,它使用的LinkedBlockingQueue;
  newSingleThreadExecutor将corePoolSize和maximumPoolSize都设置为1,也使用的LinkedBlockingQueue;
  newCachedThreadPool将corePoolSize设置为0,将maximumPoolSize设置为Integer.MAX_VALUE,使用的SynchronousQueue,也就是说来了任务就创建线程运行,当线程空闲超过60秒,就销毁线程。
  实际中,如果Executors提供的三个静态方法能满足要求,就尽量使用它提供的三个方法,因为自己去手动配置ThreadPoolExecutor的参数有点麻烦,要根据实际任务的类型和数量来进行配置。
  另外,如果ThreadPoolExecutor达不到要求,可以自己继承ThreadPoolExecutor类进行重写。

5 使用线程池实例二

package com.study.thread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
class MyRunnable implements Runnable{
	@Override
	public void run(){
		for(int i=0;i<10;i++){
			System.out.println(Thread.currentThread().getName()+":"+i);
		}
	}
}


public class ThreadpoolTest {
	public static void main(String[] args){
		ExecutorService pool=Executors.newFixedThreadPool(2);
		pool.submit(new MyRunnable());
		pool.submit(new MyRunnable());
		pool.shutdown();
		
	}
}
说明:
1) newFixedThreadPool
是固定大小的线程池 有结果可见 我们指定2 在运行时就只有2个线程工作
若其中有一个线程异常  会有新的线程替代他
 
2) shutdown方法有2个重载:
void shutdown() 启动一次顺序关闭,等待执行以前提交的任务完成,但不接受新任务。
List<Runnable> shutdownNow() 试图立即停止所有正在执行的活动任务,暂停处理正在等待的任务,并返回等待执行的任务列表。
 
3) submit 与 execute

5.1 submit是ExecutorService中的方法 用以提交一个任务
他的返回值是future对象  可以获取执行结果
<T> Future<T> submit(Callable<T> task) 提交一个返回值的任务用于执行,返回一个表示任务的未决结果的 Future。
Future<?> submit(Runnable task) 提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。
<T> Future<T> submit(Runnable task, T result) 提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。
 
5.2 execute是Executor接口的方法
他虽然也可以像submit那样让一个任务执行  但并不能有返回值
 
void execute( Runnable command)
在未来某个时间执行给定的命令。该命令可能在新的线程、已入池的线程或者正调用的线程中执行,这由 Executor 实现决定。

本文参考文章:http://www.cnblogs.com/wihainan/p/4765862.html
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值