Java 线程池
java线程池
ThreadPoolExecutor
- 初始只会创建corePoolSize个线程
- 扩容线程数的条件是,任务队列满了,如果queue很大,那么只会用核心线程池
- 超过核心线程数的线程,如果现超过最大活跃时间没有被使用,就会被回收
- 缺点:在queue没满以前只会使用核心线程数,queue满了以后创建新的线程取走一个任务,如果这时候一个任务执行很长,或者任务突然来了很多,把最大线程数也用完了,就会启用拒绝策略
public void ThreadPoolExecutor() throws InterruptedException {
LinkedBlockingQueue blockingQueue = new LinkedBlockingQueue(10);
ThreadFactory simpleThreadFactory = new SimpleThreadFactory();
DelayedAdditionRejectedExecutionHandler executionHandler = new DelayedAdditionRejectedExecutionHandler();
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1,10,3, TimeUnit.SECONDS,blockingQueue, simpleThreadFactory,executionHandler);
for(;;){
threadPoolExecutor.getRejectedExecutionHandler();
threadPoolExecutor.execute(()->{
System.out.println( Thread.currentThread().getName() +"开始" );
try {
Thread.sleep(5000L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println( Thread.currentThread().getName() +"结束" );
});
Thread.sleep(1000L);
}
}
ThreadPoolExecutor 的继承体系
- 继承关系 ThreadPoolExecutor -->AbstractExecutorService -->ExecutorService ->Executor
- ThreadPoolExecutor 对象,这个对象的 AbstractExecutorService 的 子类 ,AbstractExecutorService 是 ExecutorService 的 抽象类。
- 如果使用Executors.newxxx创建线程池,返回的都是ExecutorService 的子类。
ThreadPoolExecutor 的参数
-
corePoolSize
核心线程数,初始化的是有就有这么多个线程
-
maximumPoolSize
最大的线程数,在workQueue满了的情况开始增加任务处理能力,类似临时工
-
keepAliveTime
临时工线程,存活多久没有任务会被清除
-
TimeUnit
keepAliveTime 时间单位
-
workQueue
待处理的任务队列,也就是需要处理的任务缓冲区,当这个缓冲区满了以后,就会启用临时线程(maximumPoolSize ),队列的数量决定缓存区的大小如果缓冲区无限大,那么maximumPoolSize 就没有使用的机会了
-
threadFactory
控制线程的创建,里面只有一个方法 newThread,实现它可以控制线程怎么产生
-
RejectedExecutionHandler
当任务缓冲队列满了以后应该怎么处理,也就是拒绝策略,比如丢弃新的任务,丢弃老的任务,或者丢弃任务的时候抛出异常,或者使用主线程执行新的任务
RejectedExecutionHandler
当任务workQueue满了以后应该怎么处理,也就是拒绝策略,比如丢弃新的任务,丢弃老的任务,或者丢弃任务的时候抛出异常,或者使用主线程执行新的任务
-
AbortPolicy 抛出异常,这是大多数线程池的默认选择
-
DiscardPolicy 丢弃当前任务,实际上就是啥都没干,无视了任务满了和传递过来的任务
-
DiscardOldestPolicy 把队列中最前面的一个扔掉,然后把当前的任务放进去
-
CallerRunsPolicy 用当前线程来执行这个需要线程池执行的任务,这个一般不建议使用,因为会阻塞加入任务的主线程
我们可以通过实现RejectedExecutionHandler来实现自己的策略
比如等待一会再尝试把任务放回去,
java提供的方便创建线程池的方法
newSingleThreadExecutor
- newSingleThreadExecutor ,创建的是一个核心线程和最大线程数都是1的线程池,但是阻塞队列是几乎无穷大(Integer.MAX_VALUE)
- 也就是说,永远只有一个线程在执行任务,但是任务缓存队列是无限大的,所以几乎不会装满(装满的条件是阻塞队列满课,并且不能通过创建新的线程来减少满了的任务**)
@Test
public void newSingleThreadExecutor() throws InterruptedException {
ExecutorService threadPoolExecutor = Executors.newSingleThreadExecutor(new SimpleThreadFactory());
for(;;){
threadPoolExecutor.execute(()->{
System.out.println( Thread.currentThread().getName() +"开始" );
try {
Thread.sleep(5000L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println( Thread.currentThread().getName() +"结束" );
});
//只睡一毫秒无线添加任务,这样会创建最多5000个线程,程序不会卡死,如果不限制,如果不限制会创建无限对线程,程序卡死
Thread.sleep(1L);
}
}
newFixedThreadPool
- newFixedThreadPool**和 newSingleThreadExecutor 的区别就在线程的数量,别的都一样
@Test
public void newFixedThreadPool() throws InterruptedException {
ExecutorService threadPoolExecutor = Executors.newFixedThreadPool(10,new SimpleThreadFactory());
for(;;){
threadPoolExecutor.execute(()->{
System.out.println( Thread.currentThread().getName() +"开始" );
try {
Thread.sleep(5000L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println( Thread.currentThread().getName() +"结束" );
});
//只睡一毫秒无线添加任务,这样会创建最多5000个线程,程序不会卡死,如果不限制,如果不限制会创建无限对线程,程序卡死
Thread.sleep(1L);
}
}
newCachedThreadPool
- newCachedThreadPool 是一个核心线程数为0,最大线程数无限,阻塞队列是1的线程池。
- 也就是说这个线程池任务永远不会满,阻塞队列是1,一个任务就满了,但是这时候会启动新的线程来取走这个任务,所以永远不会满
- 优点:可以充分利用cpu资源
- 缺点:没有对线程数量加以约束,容易滥用cpu资源,如果线程数量太多,远超**cpu 数量,那么上下文切换的耗时比执行任务的时间还长。
@Test
public void newCachedThreadPool() throws InterruptedException {
ExecutorService threadPoolExecutor = Executors.newCachedThreadPool(new SimpleThreadFactory());
for(;;){
threadPoolExecutor.execute(()->{
System.out.println( Thread.currentThread().getName() +"开始" );
try {
Thread.sleep(5000L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println( Thread.currentThread().getName() +"结束" );
});
//只睡一毫秒无线添加任务,这样会创建最多5000个线程,程序不会卡死,如果不限制,如果不限制会创建无限对线程,程序卡死
Thread.sleep(1L);
}
newScheduledThreadPool
- 循环任务线程池
- 里面的任务都会被循环执行
public void newScheduledThreadPool() throws InterruptedException {
ScheduledExecutorService threadPoolExecutor = Executors.newScheduledThreadPool(10,new SimpleThreadFactory());
for(;;){
//执行下一次任务的时间是10面以后,或者当前任务执行完成以后(当前任务的执行时间大于间隔时间的时候)
threadPoolExecutor.scheduleAtFixedRate(()->{
System.out.println( Thread.currentThread().getName() +"开始" );
try {
Thread.sleep(5000L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println( Thread.currentThread().getName() +"结束" );
},3,10,TimeUnit.SECONDS);
//每当上次任务执行完毕后,间隔10秒在执行下一次任务
threadPoolExecutor.scheduleWithFixedDelay(()->{
System.out.println( Thread.currentThread().getName() +"开始" );
try {
Thread.sleep(5000L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println( Thread.currentThread().getName() +"结束" );
},3,10,TimeUnit.SECONDS);
//只睡一毫秒无线添加任务,这样会创建最多5000个线程,程序不会卡死,如果不限制,如果不限制会创建无限对线程,程序卡死
Thread.sleep(1L);
}
}
newWorkStealingPool
不指定并行数就是cpu核心数
- 这个线程池想解决核心线程数被用完,并且queue满了.才会创建新线程的问题
- 效果类似:核心线程是0,最大线程是执行的并发数,创建新线程的条件是queue不是空(而不是queue满了),执行完以后如果没有任务线程会马上回收,回收到只有一半的时候就不会回收了。
- 虽然任务队列不再是一个,而是每一个线程一个,并且线程任务队列做完会执行别的线程的任务队列,其实他们还是共用的,只是不同线程与先分配一下,如果需要执行的任务小于线程数都会被立即执行,而不是任务队列满了才启动新的线程。
@Test
public void newWorkStealingPool() throws InterruptedException {
ForkJoinPool threadPoolExecutor = (ForkJoinPool) Executors.newWorkStealingPool(10);
for(int i = 0;;i++){
//执行下一次任务的时间是10面以后,或者当前任务执行完成以后(当前任务的执行时间大于间隔时间的时候)
System.out.println("getPoolSize:" + threadPoolExecutor.getPoolSize());;
System.out.println("getParallelism:" + threadPoolExecutor.getParallelism());
threadPoolExecutor.execute(()->{
System.out.println( Thread.currentThread().getName() +"开始" );
try {
Thread.sleep(5000L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println( Thread.currentThread().getName() +"结束" );
});
System.out.println(i);
//只睡一毫秒无线添加任务,这样会创建最多5000个线程,程序不会卡死,如果不限制,如果不限制会创建无限对线程,程序卡死
if(i>10){
Thread.sleep(10000L);
}else{
Thread.sleep(1000L);
}
}
能耍的时候就一定要耍,不能耍的时候一定要学。
--天道酬勤,贵在坚持posted on 2018-07-09 16:18 zhangyukun 阅读(142) 评论(0) 收藏 举报