可能遇到的问题:
1.为什么要用线程池?避免因线程切换造成的性能损耗。
2.构造线程池时,你关注哪些参数?
3.线程池时如何接受并处理任务的?任务满了以后怎么办?
4.在使用线程池时,如何避免OOM异常?
基本用法:
定义Runnable(没有返回值)或者Callable(可以有返回值)类型的任务
创建ThreadPoolExector,注意参数
把任务放进线程池运行
通过shutdown方法关闭,即不接受新任务,当前线程执行完后退出。
class MyThread extends Thread{
private String name;
public MyThread(String name){
this.name = name;
}
public void run(){
System.out.println("Thread: " + name + " start ");
try {
sleep(5000);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("Thread "+ name + " finnish");
}
}
public class ThreadPoolDemo {
public static void main(String[] args){
//创建线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(2,4,200,
TimeUnit.MILLISECONDS,new ArrayBlockingQueue<Runnable>(5));
for (int i = 0; i < 5; ++i){
MyThread mytast = new MyThread(Integer.valueOf(i).toString());
//加入线程池
executor.execute(mytast);
}
//关闭
executor.shutdown();
}
}
运行结果:
Thread: 0 start
Thread: 1 start
Thread 0 finnish
Thread 1 finnish
Thread: 3 start
Thread: 2 start
Thread 3 finnish
Thread 2 finnish
Thread: 4 start
Thread 4 finnish
创建线程池后,线程数为0,有任务就会创建一个线程去执行,当达到corePoolSize(第一个参数:2)时,会把新的任务放到缓存队列。
maximumPlloSize(第二个参数:4)表示线程池中最多能创建多少个线程。
keepAliveTime(第三个参数:200)表示线程没有执行任务时最多保持多久时间会终止unit(第四个参数:TimeUnit.MILLISECONDS)为时间单位。workQueue(第五个参数:new ArrayBlockingQueue<Runnable>(5))为阻塞队列,用来存储等待执行的任务。
本实例没有的参数:第七个参数hander : 拒绝处理任务时的侧缺,有4个取值,常用的是丢弃任务抛异常(也是默认参数AbortPolicy),悄悄丢弃(Discardpolicy)
创建线程池后,没有线程,直到任务到来,达到设置Size后,再来就放入queue队列,如果该队列有界,最大就maxSize,如果无界,任务一直放进队列,导致OOM。
扩展顺序:新建的线程先放入线程池corePoolSize(第一个参数:2),从第三个开始放入缓冲队列workQueue(第五个参数:new ArrayBlockingQueue<Runnable>(5))。此时这个也满了,从第8个开始由这个参数(maximumPlloSize - corePoolSize)控制可建几个线程、
class Task implements Runnable{
private int num;
public Task(int num){
this.num = num;
}
public void run(){
System.out.println("the tast "+ num + " is running");
try {
Thread.currentThread().sleep(5000);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("tast " + num + " finished");
}
}
public class PoolParamDemo {
public static void main(String[] args){
ThreadPoolExecutor executor = new ThreadPoolExecutor(5,10,200,
TimeUnit.MILLISECONDS,new ArrayBlockingQueue<Runnable>(5));
for (int i = 0; i < 8; ++i){
Task task = new Task(i);
executor.execute(task);
System.out.println("The thread number in Thread Poss is:" + executor.getPoolSize() + ", the queue length is: "+
executor.getQueue().size() + ", finished thread num is: " + executor.getCompletedTaskCount());
}
executor.shutdown();
}
}
运行结果:
the tast 0 is running
The thread number in Thread Poss is:1, the queue length is: 0, finished thread num is: 0
The thread number in Thread Poss is:2, the queue length is: 0, finished thread num is: 0
the tast 1 is running
The thread number in Thread Poss is:3, the queue length is: 0, finished thread num is: 0
the tast 2 is running
The thread number in Thread Poss is:4, the queue length is: 0, finished thread num is: 0
The thread number in Thread Poss is:5, the queue length is: 0, finished thread num is: 0
the tast 3 is running
The thread number in Thread Poss is:5, the queue length is: 1, finished thread num is: 0
The thread number in Thread Poss is:5, the queue length is: 2, finished thread num is: 0
the tast 4 is running
The thread number in Thread Poss is:5, the queue length is: 3, finished thread num is: 0
tast 0 finished
the tast 5 is running
tast 2 finished
tast 1 finished
the tast 7 is running
the tast 6 is running
tast 4 finished
tast 3 finished
tast 5 finished
tast 7 finished
tast 6 finished
说辞:
消息发布模块,通过线程池处理消息。
如果不慎把workQueue设置无界会导致:
CAT监控内存,不定期内存用量偏高。
业务高峰时不定期OOM
解决:用Dump保存内存镜像,用JMAT分析,最终定位到线程池配置参数,修改为有界队列,合理设置拒绝策略。