文章参考:https://blog.csdn.net/sinat_33087001/article/details/77531013
线程池使用:
并发编程juc包学习6-线程池
为什么使用线程池?
如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。那么有没有一种办法使得线程可以复用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务?
如何创建线程池
方式一:使用构造方法.
public class ThreadPoolExecutor extends AbstractExecutorService {
.....
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue);
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory);
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler);
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler);
...
}
参数
- intcorePoolSize:核心池的大小,这个参数跟后面讲述的线程池的实现原理有非常大的关系。在创建了线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务;
- int maximumPoolSize:线程池最大线程数,这个参数也是一个非常重要的参数,它表示在线程池中最多能创建多少个线程;
- long keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用;
- TimeUnit unit:参数keepAliveTime的时间单位,有7种取值;
- BlockingQueue workQueue:一个阻塞队列,用来存储等待执行的任务,这个参数的选择也很重要;
- ThreadFactory threadFactory:线程工厂,主要用来创建线程;
- RejectedExecutionHandler handler:表示当拒绝处理任务时的策略,有以下四种取值:
-------ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
-------ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
-------ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
-------ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务;
参数的理解
- corePoolSize在很多地方被翻译成核心池大小,其实我的理解这个就是线程池的大小。举个简单的例子:假如有一个工厂,工厂里面有10个工人,每个工人同时只能做一件任务。因此只要当10个工人中有工人是空闲的,来了任务就分配给空闲的工人做;
当10个工人都有任务在做时,如果还来了任务,就把任务进行排队等待;如果说新任务数目增长的速度远远大于工人做任务的速度,那么此时工厂主管可能会想补救措施,比如重新招4个临时工人进来;然后就将任务也分配给这4个临时工人做;如果说着14个工人做任务的速度还是不够,此时工厂主管可能就要考虑不再接收新的任务或者抛弃前面的一些任务了。当这14个工人当中有人空闲时,而新任务增长的速度又比较缓慢,工厂主管可能就考虑辞掉4个临时工了,只保持原来的10个工人,毕竟请额外的工人是要花钱的。这个例子中的corePoolSize就是10,而maximumPoolSize就是14(10+4)。也就是说corePoolSize就是线程池大小。 - maximumPoolSize在我看来是线程池的一种补救措施,即任务量突然过大时的一种补救措施。不过为了方便理解,在本文后面还是将corePoolSize翻译成核心池大小。
总结一下
1.如果coolPoolSize没满的情况下,新来的任务都会创建新的线程;
2.线程数量达到coolPoolSize的时候,新来的线程会进入阻塞队列;
3.如果阻塞队列满了的话,会看是否达到了maxPoolSize,如果没到就创建新线程,并且根据keepAlive设置的闲置时间会自动销毁
4.如果队列和线程池都满了的话,会执行拒绝策略
方式二:使用Executors
➢ java.util.concurrent.Executors类可以创建线程池
➣ 创建无大小限制的线程池 :newCacheThreadPool();
➢ 创建固定大小的线程池 : newFixedThreadPool(int nThreads);
➣ 单线程池 : newSingleThreadScheduledExecutor();
➣ 创建定时调度池 : newScheduledThreadPool(int corePoolSize).
使用newFixThreadPool
public class TestThreadPool {
//延迟1s,测试线程池的容量
private static void delay() {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
static void doSome() {
delay();
System.out.println(Thread.currentThread().getName()+"执行了doSome,在"+new SimpleDateFormat("HH:mm:ss").format(new Date()));
}
}
class TestFixedThreadPool {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(6);
//最多有5个线程同时执行,超过了就进入等待队列
ExecutorService executorService = Executors.newFixedThreadPool(5);
long pre = System.currentTimeMillis();
//创建6个线程,执行会延长至下一轮
for (int i = 0; i < 6; i++) {
executorService.execute(()->{
TestThreadPool.doSome();
countDownLatch.countDown();
});
}
countDownLatch.await();
System.out.println("用时"+(System.currentTimeMillis()-pre));
//关闭线程池,不在接收新的任务
executorService.shutdown();
}
}
输出:
pool-1-thread-3执行了doSome,在11:54:01
pool-1-thread-1执行了doSome,在11:54:01
pool-1-thread-5执行了doSome,在11:54:01
pool-1-thread-4执行了doSome,在11:54:01
pool-1-thread-2执行了doSome,在11:54:01
pool-1-thread-3执行了doSome,在11:54:02
用时2177
可以看出,只有5个同时执行了,下一个等到了下一轮.这就是固定线程池
使用newCacheThreadPool
代码不用变,只需要将实现改为newCacheThreadPool.
测试结果
pool-1-thread-4执行了doSome,在11:58:07
pool-1-thread-2执行了doSome,在11:58:07
pool-1-thread-6执行了doSome,在11:58:07
pool-1-thread-5执行了doSome,在11:58:07
pool-1-thread-3执行了doSome,在11:58:07
pool-1-thread-1执行了doSome,在11:58:07
用时1151
newCacheThreadPool线程是不限制的,有一个任务就建一个线程.