線程池建立線程的方式必定效率高嗎?

java多線程的效率就必定高嗎?html

 

import java.util.LinkedList;java

public class ThreadPool extends ThreadGroup {緩存

private boolean isClosed = false; // 線程池是否關閉
private LinkedList workQueue; // 工做隊列
private static int threadPoolID = 1; // 線程池的id多線程

public ThreadPool(int poolSize) {
super(threadPoolID + "");// 指定ThreadGroup的名稱
setDaemon(true); // 繼承到的方法,設置是否守護線程池
workQueue = new LinkedList(); // 建立工做隊列
for (int i = 0; i < poolSize; i++) {
new WorkThread(i).start(); // 建立並啓動工做線程,線程池數量是多少就建立多少個工做線程
}併發

}異步

/** 等待工做線程把全部任務執行完畢 */
public void waitFinish() {
synchronized (this) {
isClosed = true;
notifyAll(); // 喚醒全部還在getTask()方法中等待任務的工做線程
}
// activeCount() 返回該線程組中活動線程的估計值。
Thread[] threads = new Thread[activeCount()];
// enumerate()方法繼承自ThreadGroup類,根據活動線程的估計值得到線程組中當前全部活動的工做線程
int count = enumerate(threads);
for (int i = 0; i < count; i++) { // 等待全部工做線程結束
try {
threads[i].join(); // 等待工做線程結束
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}性能

/** 關閉線程池 */
public synchronized void closePool() {
if (!isClosed) {
// 等待工做線程執行完畢
waitFinish();
isClosed = true;
// 清空工做隊列
workQueue.clear();
// 中斷線程池中的全部的工做線程,此方法繼承自ThreadGroup類
interrupt();
}
}測試

/** 向工做隊列中加入一個新任務,由工做線程去執行該任務 */
public synchronized void execute(Runnable task) {
if (isClosed) {
throw new IllegalStateException();
}
if (task != null) {
// 向隊列中加入一個任務
workQueue.add(task);
// 喚醒一個正在getTask()方法中待任務的工做線程
notify();
}
}this

/** 從工做隊列中取出一個任務,工做線程會調用此方法 */
private synchronized Runnable getTask(int threadid)
throws InterruptedException {
while (workQueue.size() == 0) {
if (isClosed) {
return null;
}
System.out.println("工做線程" + threadid + "等待任務...");
wait(); // 若是工做隊列中沒有任務,就等待任務
}
System.out.println("工做線程" + threadid + "開始執行任務...當前任務數:"
+ workQueue.size());
// 反回隊列中第一個元素,並從隊列中刪除
return (Runnable) workQueue.removeFirst();
}spa

/**
* 內部類,工做線程,負責從工做隊列中取出任務,並執行
*
* @author sunnylocus
*/
private class WorkThread extends Thread {
private int id;

public WorkThread(int id) {
// 父類構造方法,將線程加入到當前ThreadPool線程組中
super(ThreadPool.this, id + "");
this.id = id;
}

public void run() {
while (!isInterrupted()) { // isInterrupted()方法繼承自Thread類,判斷線程是否被中斷
Runnable task = null;
try {
task = getTask(id); // 取出任務
} catch (InterruptedException ex) {
ex.printStackTrace();
}
// 若是getTask()返回null或者線程執行getTask()時被中斷,則結束此線程
if (task == null)
return;

try {
takeTimeOperation();
// this.sleep(5000);
task.run(); // 運行任務
} catch (Throwable t) {
t.printStackTrace();
}
}// end while
}// end run
}// end workThread

// 耗時的操做,亂寫了一個
private void takeTimeOperation() {
int i = 0;
String test = "";
while (i < ThreadPoolTest.TAKETIME_NUM) {
test += "" + i;
i++;
}
}

}


---------------------
測試類:


public class ThreadPoolTest {

//線程數
private static final int THREAD_NUM = 2;

//任務數
private static final int TASK_NUM=15;

//費時的操做的時間度
public static final int TAKETIME_NUM=5500;

public static void main(String[] args) throws InterruptedException {
long beginTime = System.currentTimeMillis();
// 建立一個有THREAD_NUM個工做線程的線程池
ThreadPool threadPool = new ThreadPool(THREAD_NUM);
// 休眠500毫秒,以便讓線程池中的工做線程所有運行
Thread.sleep(500);
// 運行任務
for (int i = 0; i <= TASK_NUM; i++) { // 建立TASK_NUM個任務
threadPool.execute(createTask(i));
}
threadPool.waitFinish(); // 等待全部任務執行完畢
threadPool.closePool(); // 關閉線程池
long endTime = System.currentTimeMillis();
System.out.print("****當前線程數:" +THREAD_NUM+ ",******共用時間:" + (endTime - beginTime));

}

private static Runnable createTask(final int taskID) {
return new Runnable() {
public void run() {
// System.out.println("Task" + taskID + "開始");
System.out.println("Hello world");
// System.out.println("Task" + taskID + "結束");
}
};
}

}

 

一、若是線程操做的是耗時,可是不耗cpu的操做時,就是ThreadPool的WorkThread中將takeTimeOperation();這行註釋掉,將this.sleep(5000);開放,一樣用16個任務數測試的結果能夠發現,線程數的提升對系統的性能的改進是很明顯的。在線程數等於任務數的時候達到最高,若是線程數高於任務數就沒什麼意義了。

 

二、若是線程操做耗時,又耗cpu的狀況,就是ThreadPool的WorkThread中將takeTimeOperation();這行開放,將this.sleep(5000);註釋掉。一樣用16個任務數測試的結果能夠發現線程數的提升,系統的性能反而降低了,在線程數爲2的時候性能最好,估計是個人cpu是雙核的緣由。當線程爲1的時候對cpu的利用率不高,線程過大時cpu由於自身的壓力已經達到瓶頸了因此此時很難提升性能,雖然提升了併發,但你們的處理速度估計都慢下來了。並且線程自己也是要開銷的,固然反正用的是池技術也就是第一次建立的時候會慢一點,接下來都從池裏面取的話,對性能開銷是微乎其微的。不過線程是佔內存的,若是線程池開太大的話有內存溢出的危險。

 

三、注意:CountDownLatch異步轉同步的時候,會在全部的服務中new Thread,這樣作是最保險的?若是用線程池來作:固定大小線程池有可能併發數大的時候線程已用完而致使等待超時(等待超時的問題能夠將線程池的線程數設置爲一次任務所須要的線程數的整數倍,多少倍也就決定了併發數,這樣作很差的地方也就是線程池數設置小了沒法最大化利用硬件資源,線程池設置大了會在併發量小的時候浪費硬件資源)。無限線程池Executors.newCachedThreadPool()有可能致使內存所有消耗殆盡(雖然已作串化處理,但仍是沒法知足使用的前提:耗時較短的任務,且任務處理速度 > 任務提交速度,若是一直不回收線程建立的資源,內存會很快消耗完畢)。結論:併發執行的效率不必定比串行執行高,由於多線程在執行的時候會有個搶佔CPU資源,上下文切換的過程。詳細見線程的生命週期!

思考

一、串行執行方式對每次請求的處理須要的硬件資源是差很少的,不會出現過量消耗系統資源,那也就能夠在CountDownLatch的時候使用Executors.newCachedThreadPool()來池化線程,由於第一次請求結束第二次請求來的時候,仍是一樣的業務邏輯。且緩存線程池中的線程空閒後只會保留60秒,而固定長度線程池中的線程會一直保留,因此反而不適用。

二、注意緩存線程池的使用前提,對於長時間耗時耗CPU的計算並不適合,反而直接new Thread更加適合,詳見:爲何要使用線程池線程池的意義和生命週期

三、緩存線程池使用場景能夠用於如:request請求進入後insert流水,解析請求報文,插入串行隊列。這樣作能夠當即返回response。

相關文章
相關標籤/搜索