Java线程池ThreadPoolExecutor学习笔记java
1:核心线程:简单来说就是线程池中可否容许同时并发运行的线程的数量缓存
2:线程池大小:线程池中最多可以容纳的线程的数量。并发
3:队列:对提交过来的任务的处理模式。异步
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
corePoolSize 核心线程数,指保留的线程池大小(不超过maximumPoolSize值时,线程池中最多有corePoolSize 个线程工做)。
maximumPoolSize 指的是线程池的最大大小(线程池中最大有corePoolSize 个线程可运行)。
keepAliveTime 指的是空闲线程结束的超时时间(当一个线程不工做时,过keepAliveTime 长时间将中止该线程)。
unit 是一个枚举,表示 keepAliveTime 的单位(有NANOSECONDS, MICROSECONDS, MILLISECONDS, SECONDS, MINUTES, HOURS, DAYS,7个可选值)。
workQueue 表示存听任务的队列(存放须要被线程池执行的线程队列)。
handler 拒绝策略(当添加任务数超过maximumPoolSize+workQueue数量时报错).ide
运行时的分配机制学习
1)当池子大小小于corePoolSize就新建线程,并处理请求测试
2)当池子大小等于corePoolSize,把请求放入workQueue中,池子里的空闲线程就去从workQueue中取任务并处理this
3)当workQueue放不下新入的任务时,新建线程入池,并处理请求,若是池子大小撑到了maximumPoolSize就用RejectedExecutionHandler来作拒绝处理atom
4)另外,当池子的线程数大于corePoolSize的时候,多余的线程会等待keepAliveTime长的时间,若是无请求可处理就自行销毁.net
private ThreadPoolExecutor poolExecutor; poolExecutor = new ThreadPoolExecutor(3, 10, 1, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>(6)); for (int i = 0; i < 30; i++) { final int finalI = i; Runnable runnable = new Runnable() { @Override public void run() { Log.e("curentThread ","value: " + Thread.currentThread().getName() + "-----" + finalI); SystemClock.sleep(3000); } }; poolExecutor.execute(runnable); }
运行时会报错,由于有30个任务须要运行,而缓存队列只能缓存6个,剩下的任务须要去建立新线程,但是线程最多建立10因此就会越界报错
若是new LinkedBlockingDeque<Runnable>()则表示队列为无限大,上面运行结果为
全部的任务都存在队列中,每次只有三个线程运行,因此显示thread-1,2,3
poolExecutor = new ThreadPoolExecutor(3, 30, 1, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>(6));
若是,将maximumPoolSize改成30,则会出现20多个线程,由于队列中只存在6个任务,剩下的20多个任务须要去申请新的线程,这个能够本身运行查看效果
固定数量的线程池newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
这个线程池的核心线程与最大线程为一个值,不等待,超出核心线程必定时间后的线程就被回收掉了。最多同时运行nThreads数量的线程。
单线程池
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
核心线程为1的固定线程池
动态线程池(无界线程池)
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
核心线程池为0,线程池的最大是无限,等待时间为60秒,队列为直接提交。用的是SynchronousQueue队列,没有排队等待直接添加线程。newCachedThreadPool比较适合没有固定大小而且比较快速就能完成的小任务,不必维持一个Pool,这比直接new Thread来处理的好处是能在60秒内重用已建立的线程。机制就是查看已经建立的线程若是有空闲的(60秒之内超过就回收了)能够直接使用,不用从新建立。
线程池虽然队列能够换成无穷多个任务,可是当任务过多时就会致使内存溢出,因此当任务很是多时,要使用有界队列自定义线程池防止oom。
import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; public class CustomThreadPoolExecutor { private ThreadPoolExecutor pool = null; public void init() { pool = new ThreadPoolExecutor( 3, 10, 30, TimeUnit.MINUTES, new ArrayBlockingQueue<Runnable>(5), new CustomThreadFactory(), new CustomRejectedExecutionHandler()); } public void destory() { if(pool != null) { pool.shutdownNow(); } } public ExecutorService getCustomThreadPoolExecutor() { return this.pool; } private class CustomThreadFactory implements ThreadFactory { //原子操做 具体volatile private AtomicInteger count = new AtomicInteger(0); @Override public Thread newThread(Runnable r) { Thread t = new Thread(r); String threadName = CustomThreadPoolExecutor.class.getSimpleName() + count.addAndGet(1); System.out.println(threadName); t.setName(threadName); return t; } } private class CustomRejectedExecutionHandler implements RejectedExecutionHandler { @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { try { // 核心改造点,由blockingqueue的offer会抛弃任务,改为put阻塞方法,这种不会丢失任务 executor.getQueue().put(r); } catch (InterruptedException e) { e.printStackTrace(); } } } // 测试构造的线程池 public static void main(String[] args) { CustomThreadPoolExecutor exec = new CustomThreadPoolExecutor(); // 1.初始化 exec.init(); ExecutorService pool = exec.getCustomThreadPoolExecutor(); for(int i=1; i<100; i++) { System.out.println("提交第" + i + "个任务!"); pool.execute(new Runnable() { @Override public void run() { try { System.out.println(">>>task is running====="); TimeUnit.SECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } }); } } }
LinkedBlockingQueue添加方法put、offer、add介绍http://blog.csdn.net/z69183787/article/details/46986823
AtomicInteger介绍http://haininghacker-foxmail-com.iteye.com/blog/1401346
总结:
一、用ThreadPoolExecutor自定义线程池,看线程是的用途,若是任务量不大,能够用无界队列,若是任务量很是大,要用有界队列,防止OOM
二、若是任务量很大,还要求每一个任务都处理成功,要对提交的任务进行阻塞提交,重写拒绝机制,改成阻塞提交。保证不抛弃一个任务
三、最大线程数通常设为2N+1最好,N是CPU核数
四、核心线程数,看应用,若是是任务,一天跑一次,设置为0,合适,由于跑完就停掉了,若是是经常使用线程池,看任务量,是保留一个核心仍是几个核心线程数
五、若是要获取任务执行结果,用CompletionService,可是注意,获取任务的结果的要从新开一个线程获取,若是在主线程获取,就要等任务都提交后才获取,就会阻塞大量任务结果,队列过大OOM,因此最好异步开个线程获取结果