java线程深度解析(六)——线程池技术
2016-12-20 00:56
696 查看
http://blog.csdn.net/Daybreak1209/article/details/51382604
一种最为简单的线程创建和回收的方法:
[html] view
plain copy
new Thread(new Runnable(){
@Override
public void run() {
while(true)
{
try {
Thread.sleep(1000);//休息1s
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
在run结束后,自动回收该线程 。但在真实生产环境中,可能会开启多个线程来支撑应用系统。创建和关闭都需要花费时间,并且再生产环境中,线程的数量必须合理控制,盲目的大量创建线程,对系统性能是有伤害的。为了节省多线程并发时不断创建和销毁线程所带来的额外开销,引入线程池。
线程池的基本功能就是对线程进行复用。当接收一个任务等待处理时,先去线程池中查找是否有空闲线程,没有再创建新的线程;并且执行完毕后,将新建的线程放入线程池的空闲队列备用,而不直接销毁。
1、创建一个简易线程池
ThreadPool类:提供线程池创建、停止、添加线程、执行任务等方法
[html] view
plain copy
public class ThreadPool {
private static ThreadPool instance=null;
//空闲的县城队列
private List<PThread> idleThreads;
//已有的线程数
private int count;
private boolean isShutDown=false;
private ThreadPool()
{
this.idleThreads=new Vector(5);//类似于Arraylist
count=0;
}
public int getCreatedThread()
{
return count;
}
//取得线程实例
public synchronized static ThreadPool getInstance()
{
if(instance==null)
instance=new ThreadPool();
return instance;
}
//将线程放入线程池
public synchronized void putThread(PThread putThread)
{
if(!isShutDown)
{
idleThreads.add(putThread);
}else
{
putThread.shutDown();
}
}
//停止池中的所有线程
public synchronized void shotdown()
{
isShutDown=true;
for(int threadIndex=0;threadIndex<idleThreads.size();threadIndex++)
{
PThread idelThread=(PThread)idleThreads.get(threadIndex);
idelThread.shutDown();
}
}
//执行任务
public synchronized void start()
{
PThread thread=null;
//如果有空闲线程,直接使用
if(idleThreads.size()>0)
{
int lastIndex=idleThreads.size()-1;
thread=(PThread)idleThreads.get(lastIndex);
idleThreads.remove(lastIndex);
//立即执行这个任务
thread.setTarget(thread);
}else
{
//没有空闲线程,自己创建
count++;
thread=new PThread(target,count,this);
//启动这个线程
thread.start();
}
}
}
PThread类:模拟一个线程添加到线程池中,该线程只要不手动关闭,会一直等待任务并执行。
[html] view
plain copy
public class PThread extends Thread{
//线程池
private ThreadPool pool;
//任务
private Runnable target;
private boolean isShutDown=false;
private boolean isIdel=false;
//构造函数
public PThread(Runnable target,String name,ThreadPool pool)
{
super(name);
this.pool=pool;
this.target=target;
}
public Runnable getTarget() {
return target;
}
public boolean isIdle()
{
return isIdel;
}
@Override
public void run()
{
//只要不关闭,就一直不结束该线程
while(!isShutDown)
{
isIdel=false;
if(target !=null)
{
//运行任务
target.run();
}
//任务结束,到闲置状态
isIdel=true;
try {
//任务结束后,不关闭该线程,而放入线程池中闲置备用
pool.putThread(this);
synchronized (this) {
//线程空闲,等待任务到来
wait();
}
} catch (Exception e) {
}
isIdel=false;
}
}
public synchronized void setTarget(Runnable newTarget)
{
target=newTarget;
//设置任务之后,通知run方法,执行该任务
notifyAll();
}
//关闭线程
public synchronized void shutDown()
{
isShutDown=true;
notifyAll();
}
}
MyThread任务对象 and Client调用
2、JDK1.6中的内置线程池——Executor框架
JDK中提供了一套Executor框架支持更好的控制多线程操作。在concurrent包中是并发线程的核心,其中ThreadPoolExecutor表示一个线程池,Executors扮演者线程池工厂的角色,通过Executors可以创建特定功能的线程池。
[html] view
plain copy
//1、new一个固定线程数量的线程池,有一个新任务提交时,从该线程池中查找是否有空闲线程,如果没有新的任务会被暂存于一个任务队列中,待有空闲线程后再执行。
ExecutorService threadPoolFix=Executors.newFixedThreadPool(3);//每次3个线程,执行3个任务;其他任务等待。
//2、缓存线程池--返回一个跟任务数相等的线程数量,有多少任务就产生多少个线程进行处理。动态变化-干完了定期回收
ExecutorService threadPoolCache=Executors.newCachedThreadPool();
//3、只有一个线程的线程池,任务按照任务队列顺序FIFO执行,保证线程池始终中有一个线程,当前死了,立马搞一个顶替工作
ExecutorService threadOne=Executors.newSingleThreadExecutor();
//4、线程池大小为1,支持在给定时间执行某任务,如在某个固定的延时之后执行,或者周期性执行。-定时器效果
ExecutorService threadSingleScheduled=Executors.newSingleThreadScheduledExecutor();
//5、指定一定固定大小的线程池
ExecutorService threadScheduled=Executors.newScheduledThreadPool(5);
3、线程池优化
线程池的大小对系统性能有着很大影响,过大过小都不利于程序运行。具体多大的线程池比较合适,可以借鉴《Java并发编程实战》中提供的一个计算公式:最优线程数量=CPU的数量*CPU的使用率*[1+(等待时间/计算时间)]。在java中通过Runtime.getRuntime().availableProcessors();获取可用CPU
4、自定义线程池
jdk创建线程池对象大多继承于一个核心线程池类ThreadPoolExecutor,当采用自定义方式创建线程池时,也主要对这个类进行再封装。下面是ThreadPoolExecutor的核心构造方法:
[html] view
plain copy
public ThreadPoolExecutor(int corePoolSize,//线程池中线程数量
int maximumPoolSize,//线程池中最大线程数
long keepAliveTime,//当池中线程数超过corePoolSize时,多余线程多长时间内会被销毁
TimeUnit unit,//keepAliveTime的时间单位
BlockingQueue<Runnable> workQueue, //任务队列
RejectedExecutionHandler handler //拒绝策略,当任务太多时,如何拒绝任务) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
任务队列workqueue用于存放Runnable对象,主要有以下几种实现:
1、直接提交的队列
2、有界的任务队列
3、无界的任务队列
4、优先任务队列
拒绝策略:
1、AbortPolicy:直接抛出异常,系统停止工作
2、CallerRunsPolicy:只要线程池未关闭,直接再运行被抛弃的任务
3、DiscardOledestPolicy:丢弃最老的一个任务请求
4、DiscardPolicy:丢弃无法处理的任务,不予处理
下面模拟一个任务排序基于优先任务队列:
[html] view
plain copy
public class DefinePool implements Runnable,Comparable<DefinePool>{
protected String name;
/*
* 构造方法
*/
public DefinePool() {
}
public DefinePool(String name) {
this.name=name;
}
/*
* 模拟任务
*/
@Override
public void run() {
try {
Thread.sleep(1000);
System.out.println("模拟任务!!!!!!name:"+name);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/*
* 比较任务优先级
*/
@Override
public int compareTo(DefinePool o) {
//前提:线程名称标记优先级
int me=Integer.parseInt(this.name.split("_")[1]);
//System.out.println(me);
int other=Integer.parseInt(o.name.split("_")[1]);
if(me>other) return 1;
if(me<other) return -1;
else return 0;
}
}
客户端调用类:
[html] view
plain copy
public class DefineClient {
public static void main(String[] args) {
ExecutorService exe = new ThreadPoolExecutor(100, 200, 0L,
TimeUnit.SECONDS, new PriorityBlockingQueue<Runnable>());
for(int i=0;i<1000;i++)
{
exe.execute(new DefinePool("testPoolext_"+Integer.toString(999-i)));
}
}
}
最终输出:
模拟任务!!!!!!name:testPoolext_914
模拟任务!!!!!!name:testPoolext_915
模拟任务!!!!!!name:testPoolext_916
模拟任务!!!!!!name:testPoolext_0
模拟任务!!!!!!name:testPoolext_1
模拟任务!!!!!!name:testPoolext_3
模拟任务!!!!!!name:testPoolext_5
模拟任务!!!!!!name:testPoolext_7
模拟任务!!!!!!name:testPoolext_6
模拟任务!!!!!!name:testPoolext_4
模拟任务!!!!!!name:testPoolext_2
模拟任务!!!!!!name:testPoolext_8
模拟任务!!!!!!name:testPoolext_12
模拟任务!!!!!!name:testPoolext_11
模拟任务!!!!!!name:testPoolext_10
模拟任务!!!!!!name:testPoolext_17
模拟任务!!!!!!name:testPoolext_16
模拟任务!!!!!!name:testPoolext_14
模拟任务!!!!!!name:testPoolext_15
模拟任务!!!!!!name:testPoolext_20
从加粗部分为分界,前期程序刚启动,无需使用等待队列,故从任务序号999往下执行;当线程池中线程全部占用,任务开始进入任务队列,排队执行,使用优先任务队列,任务按照程序预定的从1~999优先级依次降低的顺序,将任务按优先级顺序依次放入任务队列。FIFO那么最先执行的就是最先放入队列的高优先级任务。
一种最为简单的线程创建和回收的方法:
[html] view
plain copy
new Thread(new Runnable(){
@Override
public void run() {
while(true)
{
try {
Thread.sleep(1000);//休息1s
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
在run结束后,自动回收该线程 。但在真实生产环境中,可能会开启多个线程来支撑应用系统。创建和关闭都需要花费时间,并且再生产环境中,线程的数量必须合理控制,盲目的大量创建线程,对系统性能是有伤害的。为了节省多线程并发时不断创建和销毁线程所带来的额外开销,引入线程池。
线程池的基本功能就是对线程进行复用。当接收一个任务等待处理时,先去线程池中查找是否有空闲线程,没有再创建新的线程;并且执行完毕后,将新建的线程放入线程池的空闲队列备用,而不直接销毁。
1、创建一个简易线程池
ThreadPool类:提供线程池创建、停止、添加线程、执行任务等方法
[html] view
plain copy
public class ThreadPool {
private static ThreadPool instance=null;
//空闲的县城队列
private List<PThread> idleThreads;
//已有的线程数
private int count;
private boolean isShutDown=false;
private ThreadPool()
{
this.idleThreads=new Vector(5);//类似于Arraylist
count=0;
}
public int getCreatedThread()
{
return count;
}
//取得线程实例
public synchronized static ThreadPool getInstance()
{
if(instance==null)
instance=new ThreadPool();
return instance;
}
//将线程放入线程池
public synchronized void putThread(PThread putThread)
{
if(!isShutDown)
{
idleThreads.add(putThread);
}else
{
putThread.shutDown();
}
}
//停止池中的所有线程
public synchronized void shotdown()
{
isShutDown=true;
for(int threadIndex=0;threadIndex<idleThreads.size();threadIndex++)
{
PThread idelThread=(PThread)idleThreads.get(threadIndex);
idelThread.shutDown();
}
}
//执行任务
public synchronized void start()
{
PThread thread=null;
//如果有空闲线程,直接使用
if(idleThreads.size()>0)
{
int lastIndex=idleThreads.size()-1;
thread=(PThread)idleThreads.get(lastIndex);
idleThreads.remove(lastIndex);
//立即执行这个任务
thread.setTarget(thread);
}else
{
//没有空闲线程,自己创建
count++;
thread=new PThread(target,count,this);
//启动这个线程
thread.start();
}
}
}
PThread类:模拟一个线程添加到线程池中,该线程只要不手动关闭,会一直等待任务并执行。
[html] view
plain copy
public class PThread extends Thread{
//线程池
private ThreadPool pool;
//任务
private Runnable target;
private boolean isShutDown=false;
private boolean isIdel=false;
//构造函数
public PThread(Runnable target,String name,ThreadPool pool)
{
super(name);
this.pool=pool;
this.target=target;
}
public Runnable getTarget() {
return target;
}
public boolean isIdle()
{
return isIdel;
}
@Override
public void run()
{
//只要不关闭,就一直不结束该线程
while(!isShutDown)
{
isIdel=false;
if(target !=null)
{
//运行任务
target.run();
}
//任务结束,到闲置状态
isIdel=true;
try {
//任务结束后,不关闭该线程,而放入线程池中闲置备用
pool.putThread(this);
synchronized (this) {
//线程空闲,等待任务到来
wait();
}
} catch (Exception e) {
}
isIdel=false;
}
}
public synchronized void setTarget(Runnable newTarget)
{
target=newTarget;
//设置任务之后,通知run方法,执行该任务
notifyAll();
}
//关闭线程
public synchronized void shutDown()
{
isShutDown=true;
notifyAll();
}
}
MyThread任务对象 and Client调用
2、JDK1.6中的内置线程池——Executor框架
JDK中提供了一套Executor框架支持更好的控制多线程操作。在concurrent包中是并发线程的核心,其中ThreadPoolExecutor表示一个线程池,Executors扮演者线程池工厂的角色,通过Executors可以创建特定功能的线程池。
[html] view
plain copy
//1、new一个固定线程数量的线程池,有一个新任务提交时,从该线程池中查找是否有空闲线程,如果没有新的任务会被暂存于一个任务队列中,待有空闲线程后再执行。
ExecutorService threadPoolFix=Executors.newFixedThreadPool(3);//每次3个线程,执行3个任务;其他任务等待。
//2、缓存线程池--返回一个跟任务数相等的线程数量,有多少任务就产生多少个线程进行处理。动态变化-干完了定期回收
ExecutorService threadPoolCache=Executors.newCachedThreadPool();
//3、只有一个线程的线程池,任务按照任务队列顺序FIFO执行,保证线程池始终中有一个线程,当前死了,立马搞一个顶替工作
ExecutorService threadOne=Executors.newSingleThreadExecutor();
//4、线程池大小为1,支持在给定时间执行某任务,如在某个固定的延时之后执行,或者周期性执行。-定时器效果
ExecutorService threadSingleScheduled=Executors.newSingleThreadScheduledExecutor();
//5、指定一定固定大小的线程池
ExecutorService threadScheduled=Executors.newScheduledThreadPool(5);
3、线程池优化
线程池的大小对系统性能有着很大影响,过大过小都不利于程序运行。具体多大的线程池比较合适,可以借鉴《Java并发编程实战》中提供的一个计算公式:最优线程数量=CPU的数量*CPU的使用率*[1+(等待时间/计算时间)]。在java中通过Runtime.getRuntime().availableProcessors();获取可用CPU
4、自定义线程池
jdk创建线程池对象大多继承于一个核心线程池类ThreadPoolExecutor,当采用自定义方式创建线程池时,也主要对这个类进行再封装。下面是ThreadPoolExecutor的核心构造方法:
[html] view
plain copy
public ThreadPoolExecutor(int corePoolSize,//线程池中线程数量
int maximumPoolSize,//线程池中最大线程数
long keepAliveTime,//当池中线程数超过corePoolSize时,多余线程多长时间内会被销毁
TimeUnit unit,//keepAliveTime的时间单位
BlockingQueue<Runnable> workQueue, //任务队列
RejectedExecutionHandler handler //拒绝策略,当任务太多时,如何拒绝任务) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
任务队列workqueue用于存放Runnable对象,主要有以下几种实现:
1、直接提交的队列
2、有界的任务队列
3、无界的任务队列
4、优先任务队列
拒绝策略:
1、AbortPolicy:直接抛出异常,系统停止工作
2、CallerRunsPolicy:只要线程池未关闭,直接再运行被抛弃的任务
3、DiscardOledestPolicy:丢弃最老的一个任务请求
4、DiscardPolicy:丢弃无法处理的任务,不予处理
下面模拟一个任务排序基于优先任务队列:
[html] view
plain copy
public class DefinePool implements Runnable,Comparable<DefinePool>{
protected String name;
/*
* 构造方法
*/
public DefinePool() {
}
public DefinePool(String name) {
this.name=name;
}
/*
* 模拟任务
*/
@Override
public void run() {
try {
Thread.sleep(1000);
System.out.println("模拟任务!!!!!!name:"+name);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/*
* 比较任务优先级
*/
@Override
public int compareTo(DefinePool o) {
//前提:线程名称标记优先级
int me=Integer.parseInt(this.name.split("_")[1]);
//System.out.println(me);
int other=Integer.parseInt(o.name.split("_")[1]);
if(me>other) return 1;
if(me<other) return -1;
else return 0;
}
}
客户端调用类:
[html] view
plain copy
public class DefineClient {
public static void main(String[] args) {
ExecutorService exe = new ThreadPoolExecutor(100, 200, 0L,
TimeUnit.SECONDS, new PriorityBlockingQueue<Runnable>());
for(int i=0;i<1000;i++)
{
exe.execute(new DefinePool("testPoolext_"+Integer.toString(999-i)));
}
}
}
最终输出:
模拟任务!!!!!!name:testPoolext_914
模拟任务!!!!!!name:testPoolext_915
模拟任务!!!!!!name:testPoolext_916
模拟任务!!!!!!name:testPoolext_0
模拟任务!!!!!!name:testPoolext_1
模拟任务!!!!!!name:testPoolext_3
模拟任务!!!!!!name:testPoolext_5
模拟任务!!!!!!name:testPoolext_7
模拟任务!!!!!!name:testPoolext_6
模拟任务!!!!!!name:testPoolext_4
模拟任务!!!!!!name:testPoolext_2
模拟任务!!!!!!name:testPoolext_8
模拟任务!!!!!!name:testPoolext_12
模拟任务!!!!!!name:testPoolext_11
模拟任务!!!!!!name:testPoolext_10
模拟任务!!!!!!name:testPoolext_17
模拟任务!!!!!!name:testPoolext_16
模拟任务!!!!!!name:testPoolext_14
模拟任务!!!!!!name:testPoolext_15
模拟任务!!!!!!name:testPoolext_20
从加粗部分为分界,前期程序刚启动,无需使用等待队列,故从任务序号999往下执行;当线程池中线程全部占用,任务开始进入任务队列,排队执行,使用优先任务队列,任务按照程序预定的从1~999优先级依次降低的顺序,将任务按优先级顺序依次放入任务队列。FIFO那么最先执行的就是最先放入队列的高优先级任务。
相关文章推荐
- java线程深度解析(六)——线程池技术
- java线程深度解析(二)——线程互斥技术与线程间通信
- java线程深度解析(二)——线程互斥技术与线程间通信
- java线程深度解析(一)——java new 接口?匿名内部类给你答案
- java线程深度解析(四)——并发模型(Master-Worker)
- java线程深度解析(五)——并发模型(生产者-消费者)
- 深度解析Java线程池的异常处理机制
- java线程深度解析(五)——并发模型(生产者-消费者)
- java线程深度解析(三)——并发模型(Future)
- Java线程池ThreadPoolExecutor深度探索及源码解析
- 使用新的java线程池技术创建固定的线程去完成任务,都完成后计算总时间
- Java线程池ThreadPoolExecutor深度探索及源码解析
- java线程深度解析(七)——并发数据结构
- java线程深度解析(一)——java new 接口?匿名内部类给你答案
- java线程深度解析(七)——并发数据结构
- java线程深度解析(三)——并发模型(Future)
- JAVA并发API源码解析:并发数据结构、线程、线程池及其应用
- java线程深度解析(四)——并发模型(Master-Worker)
- Java线程知识深度解析
- 深度解析MFC线程及机制