標籤:ram locking 取出 com exe eject 靈活 pool execution
在沒有看不論什麼代碼之前首先想一下線程池應該有哪幾部分:
任務隊列非常好辦。直接用堵塞隊列就能夠了:BlockingQueue<Runnable> workQueue。而線程是用來運行任務的,那麼理所當然應該是不斷地從任務隊列中取出任務來運行,我們來看ThreadPoolExecutor中的Worker的實現:
private final class Worker implements Runnable {private final ReentrantLock runLock = new ReentrantLock();private Runnable firstTask;volatile long completedTasks;Thread thread;// 運行taskprivate void runTask(Runnable task) {final ReentrantLock runLock = this.runLock;runLock.lock();try {// 檢查線程池的狀態,推斷是否中斷if (runState < STOP && Thread.interrupted() && runState >= STOP)thread.interrupt();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;// 從線程池中取出Runnable然後運行while (task != null || (task = getTask()) != null) {runTask(task);task = null;}} finally {workerDone(this);}}}
通過推斷runLock是不是鎖上的狀態就能夠推斷Worker是否在運行任務了。當然getTask失敗的時候,Worker的任務也就結束了,我們有時候會希望線程去等一段時間,假設這段時間裡面沒有任務到達線程才退出。
這個在堵塞隊列中有線程的方法。在getTask能夠看到,例如以下:
r = workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS);
在拿到任務的時候,假設如今線程池還非常小,那麼如今不須要將任務放到堵塞隊列裡面去,直接建立一個線程執行就能夠了。在這條路走不通的時候才會“繞彎路”來做。由於堵塞隊列也是有容量限制的,那麼在嘗試將任務放進去的時候可能會失敗。
假設讓我寫的話就不會嘗試了,而是直接調用堵塞方法讓主線程堵塞在這裡,這樣事實上並不好:
- 不夠靈活
- 調用的線程被堵塞了,浪費資源,事實上它是能夠用來運行任務的
假設自己要實現reject策略的話實現以下介面就能夠了:
public interface RejectedExecutionHandler {void rejectedExecution(Runnable r, ThreadPoolExecutor executor);}
有可能出現一種情況:插入任務之後線程池的狀態改變了。那麼也要保證該任務可以被處理(並不一定是任務被完畢。也可能是拒絕),那麼以下來看提交任務的完整邏輯:
public void execute(Runnable command) {if (command == null)throw new NullPointerException();// 嘗試建立線程並運行任務if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) {// 嘗試將線程放入堵塞隊列if (runState == RUNNING && workQueue.offer(command)) {if (runState != RUNNING || poolSize == 0)// 確保任務會被處理ensureQueuedTaskHandled(command);} else if (!addIfUnderMaximumPoolSize(command))// 假設如今能建立一個線程運行該任務的話,就不要拒絕它了。。。reject(command);}}
能夠發如今線程池執行期間改動各種閥值都是能夠起到作用的。假設在spring中想用簡單的線程池的話沒有必要自己寫一個,直接用現成的配置一個就能夠了,比方:
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"><property name="corePoolSize" value="5" /><property name="maxPoolSize" value="10" /><property name="queueCapacity" value="25" /></bean>
---------- ---------- ---------- ---------- END ---------- ---------- ---------- ----------
[Java]線程池