1. 程式人生 > >[Java]線程池

[Java]線程池

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]線程池