并发编程-Java线程池
一、简述
线程频繁的创建和销毁造成资源的浪费,如果是在并发情况下,这种频繁的操作会对性能影响非常大。
为了避免线程的频繁创建与销毁,可以创建一定数量的线程集合,在需要使用时,从集合里获取线程;使用完后再将线程放回集合中,实现线程的重复利用。而这个集合就是要说的线程池。
线程池可以提高响应速度(减少创建线程的时间)、降低资源消耗、便于进行线程管理
二、常见的线程池
- newCachedThreadPool
- newFixedThreadPool
- newScheduledThreadPool
- newSingleThreadExcutor
2.1、线程类
为了使下面的代码更好理解,将任务代码单独写成一个类
public class MyThread implements Runnable{
@Override
public void run() {
System.out.println("线程"+Thread.currentThread().getName()+"正在执行");
try {
//模拟线程执行
Thread.sleep(2000);
Random r = new Random();
if(1 == r.nextInt(2)){
System.out.println("任务中断");
Thread.currentThread().stop();
throw new Exception("任务中断");
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("线程"+Thread.currentThread().getName()+"执行完成");
}
}
2.2、newCachedThreadPool
- 创建一个可缓存扩展的线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
- 工作线程的创建数量最大值为Integer.MAX_VALUE。
- 如果长时间没有往线程池中提交任务,即如果工作线程持续空闲了指定的时间(默认为1分钟),则该工作线程将自动终止,终止后,如果又提交了新的任务,则线程池重新创建一个工作线程。
- 一定要注意控制任务的数量,否则,由于大量线程同时运行,很快就会造成系统瘫痪。
public static void main(String[] args) {
//创建一个可缓存扩展的线程池
ExecutorService cachethreadPool = Executors.newCachedThreadPool();
try {
for (int i = 0; i < 20; i++) {
MyThread myThread = new MyThread();
//提交任务
cachethreadPool.execute(new Thread(myThread));
}
}catch (Exception e){
e.printStackTrace();
}finally {
//关闭线程池
cachethreadPool.shutdown();
}
}
2.3、newFixedThreadPool
- 创建一个指定工作线程数量的线程池,每当提交一个任务就创建一个工作线程,如果工作线程数量达到线程池指定的最大值,则将提交的任务存入到等待池队列中。
- 提高程序效率和节省创建线程时所耗的开销。
- 在线程池空闲时,即线程池中没有可运行的任务、或者可运行的任务远远小于线程池的已有工作线程的数量时,它不会释放已创建的工作线程,这会导致占用一定的系统资源。
public static void main(String[] args) {
//创建一个线程数量为5的线程池
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
try {
for (int i = 0; i < 20; i++) {
MyThread myThread = new MyThread();
//提交20个任务
fixedThreadPool.execute(new Thread(myThread));
}
}catch (Exception e){
e.printStackTrace();
}finally {
//关闭线程池
fixedThreadPool.shutdown();
}
}
2.4、newScheduledThreadPool
- 创建一个指定工作线程数量的线程池,而且支持定时的以及周期性的任务执行。
public static void main(String[] args) {
//创建一个可定时的线程并且数量为5的线程池
ScheduledExecutorService scheduledThreadPool = null;
scheduledThreadPool = Executors.newScheduledThreadPool(5);
try {
for (int i = 0; i < 20; i++) {
MyThread myThread = new MyThread();
//提交任务,并延迟3秒执行
scheduledThreadPool.schedule(new Thread(myThread),3, TimeUnit.SECONDS);
//提交任务,第一次延迟3秒执行,结束后间隔2秒执行一次
//scheduledThreadPool.scheduleAtFixedRate(new Thread(myThread),3,2,TimeUnit.SECONDS);
//提交任务,第一次延迟3秒执行,执行后间隔2秒执行一次
//scheduledThreadPool.scheduleWithFixedDelay(new Thread(myThread),3,2,TimeUnit.SECONDS);
}
}catch (Exception e){
e.printStackTrace();
}finally {
//关闭线程池(执行周期性任务时不能关闭线程池)
//scheduledThreadPool.shutdown();
}
}
2.4、newSingleThreadExcutor
- 创建一个单线程的Executor,即只创建一个工作线程来执行任务,它只会用唯一的工作线程来执行任务,以保证所有任务按照指定顺序(FIFO,LIFO)执行。
- 如果这个线程任务异常结束,会有另一个取代,保证任务执行。
- 单个工作线程最大的特点就是可保证顺序地执行各个任务,并且在任意给定的时间内不会有多个线程同时活动。
public static void main(String[] args) {
//创建一个可定时的线程并且数量为5的线程池
ExecutorService singleThreadPool = null;
singleThreadPool = Executors.newSingleThreadScheduledExecutor();
try {
for (int i = 0; i < 20; i++) {
MyThread myThread = new MyThread();
//提交任务
singleThreadPool.execute(new Thread(myThread));
}
}catch (Exception e){
e.printStackTrace();
}finally {
//关闭线程池
singleThreadPool.shutdown();
}
}
三、自定义线程池
3.1、线程池的实现
通过看源码,可以发现线程池都是通过调用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;
}
3.2、线程池的7大参数
从实现方法中可以看到有七个参数来设计线程池
- corePoolSize: 核心线程数量,核心线程会一直存在,除非allowCoreThreadTimeOut设置为true。
- maximumPoolSize: 线程池允许最大线程数量。
- keepAliveTime: 已创建线程数量超过corePoolSize时,空闲线程的最大存活超时时间。
- unit: 超时时间单位。
- workQueue: 工作等候队列,保存未执行的Runnable任务。
- threadFactory: 创建线程池中工作线程的工厂。
- handler: 当线程已满,工作等候队列也满了的时候,将会调用这个方法来实现各种拒绝策略。
3.3、自定义一个线程池
可以通过自定义线程池的参数来定制化线程池
public static void main(String[] args) {
ThreadFactory myThreadFactory = new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
return null;
}
};
RejectedExecutionHandler myHandle = new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
//拒绝策略
}
};
ExecutorService myThreadPool = new ThreadPoolExecutor(2,5,3,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(6),
myThreadFactory,myHandle);
}