关于java线程池
为什么要用线程池
1频繁的new或者销毁线程,是耗费资源和花时间
2如果创建太多的线程,程序内存会很轻易崩溃。
ExecutorService service = Executors.newFixedThreadPool(6); //分配执行任务量 service.execute(new Runnable() { @Override public void run() { }})
这是线程池的常用方法,Executors 属于concurrent包下
public class Executors { //指定线程 public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); } } /**ThreadPoolExecutor的一个构造方法*/ public ThreadPoolExecutor(int corePoolSize/**核心线程数*/, int maximumPoolSize/**最大线程数*/, long keepAliveTime/**活跃时间用于后面shutdown线程*/, TimeUnit unit, BlockingQueue<Runnable> workQueue/**任务队列*/) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler); }
execute方法
public void execute(Runnable command) { if (command == null) throw new NullPointerException(); if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)/*new出新的线程来执行*/) { //当前线程数大于corePoolSize,走这里,如果状态满足,加入到workqueue中 if (runState == RUNNING && workQueue.offer(command)) { if (runState != RUNNING || poolSize == 0) ensureQueuedTaskHandled(command); } //如果workqueue满了,执行addIfUnderMaximumPoolSize 或者拒绝 else if (!addIfUnderMaximumPoolSize(command)) reject(command); // is shutdown or saturated } } //如果poolsize小于CorePoolSize private boolean addIfUnderCorePoolSize(Runnable firstTask) { Thread t = null; //重入锁 final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { if (poolSize < corePoolSize && runState == RUNNING) //就是new出一个线程,来执行任务 t = addThread(firstTask); } finally { mainLock.unlock(); } if (t == null) return false; //就执行任务 t.start(); return true; } //就是new出一个线程,来执行任务 private Thread addThread(Runnable firstTask) { Worker w = new Worker(firstTask); Thread t = threadFactory.newThread(w); if (t != null) { w.thread = t; workers.add(w); int nt = ++poolSize; if (nt > largestPoolSize) largestPoolSize = nt; } return t; } //当poolsize介于corepoolsize和maxinumpoolsize之间的时候 private boolean addIfUnderMaximumPoolSize(Runnable firstTask) { Thread t = null; final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { if (poolSize < maximumPoolSize && runState == RUNNING) t = addThread(firstTask); } finally { mainLock.unlock(); } if (t == null) return false; t.start(); return true; }
以上代码是线程池的执行流程
然后看一看线程池的工作机制
上面涉及一个类
Worker w = new Worker(firstTask);
//Worker类源码 private final class Worker implements Runnable { private final ReentrantLock runLock = new ReentrantLock(); private Runnable firstTask; volatile long completedTasks; Thread thread; Worker(Runnable firstTask) { this.firstTask = firstTask; } boolean isActive() { return runLock.isLocked(); } void interruptIfIdle() { final ReentrantLock runLock = this.runLock; if (runLock.tryLock()) { try { if (thread != Thread.currentThread()) thread.interrupt(); } finally { runLock.unlock(); } } } void interruptNow() { thread.interrupt(); } private void runTask(Runnable task) { final ReentrantLock runLock = this.runLock; runLock.lock(); try { if (runState < STOP && Thread.interrupted() && runState >= STOP) boolean ran = false; beforeExecute(thread, task); try { task.run(); ran = true; afterExecute(task, null); ++completedTasks; } catch (RuntimeException ex) { if (!ran) afterExecute(task, ex); throw ex; } } finally { runLock.unlock(); } } public void run() { try { Runnable task = firstTask; firstTask = null; //所以这是一个循环,当firstTask执行完的时候,线程会去不停的取任务 while (task != null || (task = getTask()) != null) { //执行任务 runTask(task); task = null; } } finally { workerDone(this); } } } Runnable getTask() { for (;;) { try { int state = runState; if (state > SHUTDOWN) return null; Runnable r; if (state == SHUTDOWN) r = workQueue.poll(); else if (poolSize > corePoolSize || allowCoreThreadTimeOut) //从刚才的任务队列中取出任务,并返回 r = workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS); else r = workQueue.take(); if (r != null) return r; if (workerCanExit()) { if (runState >= SHUTDOWN) interruptIdleWorkers(); return null; } } catch (InterruptedException ie) { } } }
Worker继承自Runnable,所以关键是run方法,run中看出,这个方法是while循环,会不停的去拿任务执行。
分割线
平常,在指定线程数的时候,如何衡量指定线程数。
一般程序执行分为IO密集型和CPU密集型
- CPU密集型:长时间占用cpu 这时候多NEW出一些线程就是狼辉。数量为cpu核数N
- IO密集型:短暂占用cpu,IO操作占用时间 这样就可以多NEW出一些线程。数量为cpu核数N*
当任务过多,队列满了后,maxcoresize的线程都处理不了时,会启用线程池的拒绝策略。拒绝策略分四个:
- 直接丢弃任务。直接报错。慎用啊
- 用当前线程执行,那当前线程都阻塞了 业务也不用执行拉,慎用啊
- 删除队列的头部任务,然后重新尝试任务。慎用啊
- 直接丢弃任务。不报错。
其实拒绝策略和任务排队策略是相关的,排队策略分:
- 有限队列。这就需要指定maxcoresize和拒绝策略。
- 无限队列。无限队列,就是队列可以不断扩充,那就不会拒绝了,也不用maxsize参数。linkedblockingqueue
下面是threadpoolExecetor构造函数
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {}
可以指定排队策略,拒绝策略。
默认情况用的LinkedBlockingQueue 为无界队列。默认无限扩充队列长度。最多跑过200多w个任务 单独写了个程序跑。没有什么问题