要用到线程池,就要先介绍一个类:ThreadPoolExecutor.
通过查看源码发现继承及实现关系为
在ThreadPoolExecutor类中,发现有四种构造方法,需要传入很多参数。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
实例化一个ThreadPoolExecutor类
ThreadPoolExecutor executor=new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>,5));
核心池大小为5,线程池大小为10,线程允许空闲200ms,阻塞队列大小5.
然后,创建20个线程并每次打印线程池中的状态
for(int a=0;a<20;a++) {
executor.execute(new ThreadPoolT(a));
System.out.println("线程池中线程数目"+executor.getPoolSize()+"正在等待执行的任务数目"+executor.getQueue().size()+"已经执行完的任务数目:"+executor.getCompletedTaskCount());
}
其中线程的run方法为
@Override
public void run() {
// TODO Auto-generated method stub
System.out.print("线程"+id+"正在执行...");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("线程"+id+"结束..");
}
关闭线程池
executor.shutdown();
总代码
package com.test;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPoolT implements Runnable{
private int id;
public ThreadPoolT(int id) {
super();
this.id = id;
}
public static void main(String[] args) {
ThreadPoolExecutor executor=new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(5));
for(int a=0;a<20;a++) {
executor.execute(new ThreadPoolT(a));
System.out.println("线程池中线程数目"+executor.getPoolSize()+"正在等待执行的任务数目"+executor.getQueue().size()+"已经执行完的任务数目:"+executor.getCompletedTaskCount());
}
System.out.println(executor.getLargestPoolSize());
System.out.println(executor.getPoolSize());
executor.shutdown();
}
@Override
public void run() {
// TODO Auto-generated method stub
System.out.print("线程"+id+"正在执行...");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("线程"+id+"结束..");
}
}
可见
如果把前面的队列里的参数5去掉,并在线程池结束前加下列代码:
Thread.sleep(10000);
System.out.println(executor.getPoolSize()+"AAAAAAAAAAAAAAA");
executor.shutdown();
则运行结果为:
可见线程全部运行结束后核心池仍有5个线程,这便是线程池使用的优点,不用频繁开线程关线程,而是提供一些线程代理运行需要运行的线程,提高了程序效率。
另外,其实一般使用线程池不用我们每次指定那么多参数。一般使用Executors类中提供的静态方法来创建线程池:
//corePoolSize和maximumPoolSize值相等,它使用LinkedBlockingQueue
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
}
//corePoolSize和maximumPoolSize都设置为1,使用LinkedBlockingQueue
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
//corePoolSize设置为0,maximumPoolSize设置为Integer.MAX_VALUE,使用SynchronousQueue,也就是说来了任务就创建线程运行,当线程空闲超过60秒,就销毁线程
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
(1)高并发、任务执行时间短的业务,线程池线程数可以设置为CPU核数+1,减少线程上下文的切换
(2)并发不高、任务执行时间长的业务要区分开看:
a)假如是业务时间长集中在IO操作上,也就是IO密集型的任务,因为IO操作并不占用CPU,所以不要让所有的CPU闲下来,可以适当加大线程池中的线程数目,让CPU处理更多的业务 (2*cpu核心)
b)假如是业务时间长集中在计算操作上,也就是计算密集型任务,这个就没办法了,和(1)一样吧,线程池中的线程数设置得少一些,减少线程上下文的切换