Java线程池
来源:互联网 发布:相机cf卡数据恢复 编辑:程序博客网 时间:2024/05/08 08:57
线程池的关闭
我们可以通过调用线程池的shutdown方法或者shutdownNow方法来关闭线程池,其原理是遍历线程池中的所有工作线程,然后逐个调用线程的interrupt方法来中断线程。这也就意味着无法响应中断的线程可能就无法终止。
shutdown方法按顺序中断之前已提交的任务,同时拒绝接收新的任务。这个方法不会等待之前已提交任务执行完成,可通过调用awaitTermination方法去完成这种功能。已shut down的线程池再调用此方法没有任何影响。
shutdownNow方法尝试停止所有正在执行的任务,终止正在等待的任务,并返回所有正在等待的任务。
只要调用了以上俩方法中任何一个,isShutdown方法都会返回true。当所有任务都已经终止后,才表示线程池关闭成功,此时isTerminated方法会返回true。
线程池的工作流程如下:
从图中我们可以看出,当一个任务被提交到线程池后,线程池的处理流程如下:
- 首先判断基本线程池有没有满?如果没满,则创建一个线程来执行任务,否则进入下一流程;
- 判断工作队列是否已满?如果没满,将任务存储在工作队列中,否则进入下一流程;
- 判断线程池是否已满?如果没满,并且当前线程池的工作线程的数量小于最大线程池数量,则创建新线程,否则提交给饱和处理策略来处理这个任务。
ThreadPoolExecutor的线程池处理流程的源码如下:
public void execute(Runnable command) { if (command == null) throw new NullPointerException(); /* * Proceed in 3 steps: * * 1. If fewer than corePoolSize threads are running, try to * start a new thread with the given command as its first * task. The call to addWorker atomically checks runState and * workerCount, and so prevents false alarms that would add * threads when it shouldn't, by returning false. * * 2. If a task can be successfully queued, then we still need * to double-check whether we should have added a thread * (because existing ones died since last checking) or that * the pool shut down since entry into this method. So we * recheck state and if necessary roll back the enqueuing if * stopped, or start a new thread if there are none. * * 3. If we cannot queue task, then we try to add a new * thread. If it fails, we know we are shut down or saturated * and so reject the task. */ int c = ctl.get(); if (workerCountOf(c) < corePoolSize) { if (addWorker(command, true)) return; c = ctl.get(); } if (isRunning(c) && workQueue.offer(command)) { int recheck = ctl.get(); if (! isRunning(recheck) && remove(command)) reject(command); else if (workerCountOf(recheck) == 0) addWorker(null, false); } else if (!addWorker(command, false)) reject(command);}进入addWorker方法:
private boolean addWorker(Runnable firstTask, boolean core) { retry: for (;;) { int c = ctl.get(); int rs = runStateOf(c); // Check if queue empty only if necessary. if (rs >= SHUTDOWN && ! (rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty())) return false; for (;;) { int wc = workerCountOf(c); if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize)) return false; if (compareAndIncrementWorkerCount(c)) break retry; c = ctl.get(); // Re-read ctl if (runStateOf(c) != rs) continue retry; // else CAS failed due to workerCount change; retry inner loop } } boolean workerStarted = false; boolean workerAdded = false; Worker w = null; try { w = new Worker(firstTask); final Thread t = w.thread; if (t != null) { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { // Recheck while holding lock. // Back out on ThreadFactory failure or if // shut down before lock acquired. int rs = runStateOf(ctl.get()); if (rs < SHUTDOWN || (rs == SHUTDOWN && firstTask == null)) { if (t.isAlive()) // precheck that t is startable throw new IllegalThreadStateException(); workers.add(w); int s = workers.size(); if (s > largestPoolSize) largestPoolSize = s; workerAdded = true; } } finally { mainLock.unlock(); } if (workerAdded) { t.start(); workerStarted = true; } } } finally { if (! workerStarted) addWorkerFailed(w); } return workerStarted;}可以看到,在线程池中创建线程时,会将任务封装成工作线程Worker。并且当工作线程Worker执行完任务之后,会将其添加到workers集合中。在Worker的run方法中,将会调用runWorker方法并将自己传入,源码如下:
final void runWorker(Worker w) { Thread wt = Thread.currentThread(); Runnable task = w.firstTask; w.firstTask = null; w.unlock(); // allow interrupts boolean completedAbruptly = true; try { while (task != null || (task = getTask()) != null) { w.lock(); // If pool is stopping, ensure thread is interrupted; // if not, ensure thread is not interrupted. This // requires a recheck in second case to deal with // shutdownNow race while clearing interrupt if ((runStateAtLeast(ctl.get(), STOP) || (Thread.interrupted() && runStateAtLeast(ctl.get(), STOP))) && !wt.isInterrupted()) wt.interrupt(); try { beforeExecute(wt, task); Throwable thrown = null; try { task.run(); } catch (RuntimeException x) { thrown = x; throw x; } catch (Error x) { thrown = x; throw x; } catch (Throwable x) { thrown = x; throw new Error(x); } finally { afterExecute(task, thrown); } } finally { task = null; w.completedTasks++; w.unlock(); } } completedAbruptly = false; } finally { processWorkerExit(w, completedAbruptly); }}由上可以看到,Worker将会通过循环不断从任务队列中获取任务执行。
配置线程池
通常配置线程池需要考虑几个方面:
- 任务的性质:CPU密集型,IO密集型还是混合型任务;
- 任务的优先级;
- 任务执行时间长短;
- 任务是否依赖于其它的系统资源;
可通过Runtime.getRuntime().availableProcessors()方法获取当前机器的CPU数量。
优先级不同的任务可以将其工作队列设为优先级队列PriorityBlockingQueue。
监控线程池
可通过继承线程池并重写线程池的一些方法,比如beforeExecute,afterExecute以及terminated等方法,在任务执行前,任务执行后以及线程池关闭后实现一些自己的逻辑,如监控任务的平均执行时间等等。这些方法都是空的,且为protected,源码如下:
protected void afterExecute(Runnable r, Throwable t) { }
protected void terminated() { }
protected void beforeExecute(Thread t, Runnable r) { }
此外线程池里还有一些属性也可以在监控时使用,比如taskCount表示线程池需要持续任务的数量等。
参考资料:
- JDK8源码
- Java并发编程实战
- Java线程:线程池
- java--线程--线程池
- Java线程(六):线程池
- Java线程(五):线程池
- Java线程(五):线程池
- Java线程_07_线程池
- Java线程(五):线程池
- Java线程(六):线程池
- Java线程(五):线程池
- Java线程(六):线程池
- Java线程(六):线程池
- Java线程(六):线程池
- Java线程(六):线程池
- Java线程(六):线程池
- Java线程(六):线程池
- Java线程(六):线程池
- Java线程(五):线程池
- Java线程(六):线程池
- ceph 中的performance counter
- Mina IoFilter注意事项说明
- HDU 2203 亲和串
- hdu1203-01背包
- 学习类的继承的感想
- Java线程池
- TreeBidiMap实现key和value的互相读取
- C#中MessageBox用法大全
- tomcat7-maven-plugin插件把web应用一键式的部署到一个远程的tomcat中
- 转载 TextInputLayout
- 我的第一个C++程序
- laravel视图提交表单到控制器的使用需要注意配置事项
- C语言和c++中的const、static、extern
- 矩阵维度与通道