[Java]線程池
阿新 • • 發佈:2017-06-05
sta 狀態 lsi fun man his prop () mil
在沒有看不論什麽代碼之前首先想一下線程池應該有哪幾部分:
- 任務隊列
- 線程
任務隊列非常好辦,直接用堵塞隊列就能夠了: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; // 運行task private 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]線程池