java之線程池簡單實現

來源:互聯網
上載者:User

  以前做的東西,實現一個簡單的多線程機制,開始之前,現說說原理性的東西吧,下面是我在ibm開發人員上搜到的內容

線程池的技術背景

  在物件導向編程中,建立和銷毀對象是很費時間的,因為建立一個對象要擷取記憶體資源或者其它更多資源。在Java中更是如此,虛擬機器將試圖跟蹤每一個對象, 以便能夠在對象銷毀後進行記憶體回收。所以提高服務程式效率的一個手段就是儘可能減少建立和銷毀對象的次數,特別是一些很耗資源的對象建立和銷毀。如何利用 已有對象來服務就是一個需要解決的關鍵問題,其實這就是一些"池化資源"技術產生的原因。

  多線程技術主要解決處理器單元內多個線程執行的問題,它可以顯著減少處理器單元的閑置時間,增加處理器單元的吞吐能力。但如果對多線程應用不當,會增加對單個任務的處理時間。可以舉一個簡單的例子:

  假設在一台伺服器完成一項任務的時間為T

      T1 建立線程的時間      T2 線上程中執行任務的時間,包括線程間同步所需時間      T3 線程銷毀的時間

  顯然T = T1+T2+T3。注意這是一個極度簡化的假設。

  可以看出T1,T3是多線程本身的帶來的開銷,我們渴望減少T1,T3所用的時間,從而減少T的時間。但一些線程的使用者並沒有注意到 這一點,所以在程式中頻繁的建立或銷毀線程,這導致T1和T3在T中佔有相當比例。顯然這是突出了線程的弱點(T1,T3),而不是優點(並發性)。

  線程池技術正是關注如何縮短或調整T1,T3時間的技術,從而提高伺服器程式效能的。它把T1,T3分別安排在伺服器程式的啟動和結束的時間段或者一些閒置時間段,這樣在伺服器程式處理客戶請求時,不會有T1,T3的開銷了。

  線程池不僅調整T1,T3產生的時間段,而且它還顯著減少了建立線程的數目。在看一個例子:

  假設一個伺服器一天要處理50000個請求,並且每個請求需要一個單獨的線程完成。我們比較利用線程池技術和不利於線程池技術的伺服器 處理這些請求時所產生的線程總數。線上程池中,線程數一般是固定的,所以產生線程總數不會超過線程池中線程的數目或者上限(以下簡稱線程池尺寸),而如果 伺服器不利用線程池來處理這些請求則線程總數為50000。一般線程池尺寸是遠小於50000。所以利用線程池的伺服器程式不會為了建立50000而在處 理請求時浪費時間,從而提高效率。

  這些都是假設,不能充分說明問題,下面我將討論區對話池的簡單實現並對該程式進行對比測試,以說明線程技術優點及應用領域。

一般一個簡單線程池至少包含下列組成部分

  • 線程池管理器(ThreadPoolManager):用於建立並管理線程池
  • 背景工作執行緒(WorkThread): 線程池中線程
  • 任務介面(Task):每個任務必須實現的介面,以供背景工作執行緒調度任務的執行。
  • 任務隊列:用於存放沒有處理的任務。提供一種緩衝機制。

  線程池管理器至少有下列功能:建立線程池,銷毀線程池,添加新任務。下面就是小弟的實現,還是歡迎拍磚哈:

public class ThreadPoolManager {
private static ThreadPoolManager instance = null;
private List<Upload> taskQueue = Collections.synchronizedList(new LinkedList<Upload>());//任務隊列
private WorkThread[] workQueue ; //背景工作執行緒(真正執行任務的線程)
private static int worker_num = 5; //背景工作執行緒數量(預設背景工作執行緒數量是5)
private static int worker_count = 0;

private ThreadPoolManager(){
this(5);
}
private ThreadPoolManager(int num){
worker_num = num;
workQueue = new WorkThread[worker_num];
for(int i=0;i<worker_num;i++){
workQueue[i] = new WorkThread(i);
}
}

public static synchronized ThreadPoolManager getInstance(){
if(instance==null)
instance = new ThreadPoolManager();
return instance;
}

public void addTask(Upload task){
//對任務隊列的操作要上鎖
synchronized (taskQueue) {
if(task!=null){
taskQueue.add(task);
taskQueue.notifyAll();
System.out.println("task id "+task.getInfo() + " submit!");
}

}
}

public void BatchAddTask(Upload[] tasks){
//對任務隊列的修改操作要上鎖
synchronized (taskQueue) {
for(Upload e:tasks){
if(e!=null){
taskQueue.add(e);
taskQueue.notifyAll();
System.out.println("task id "+e.getInfo() + " submit!");
}
}
}
}

public void destory(){
System.out.println("pool begins to destory ...");
for(int i = 0;i<worker_num;i++){
workQueue[i].stopThread();
workQueue[i] = null;
}
//對任務隊列的操作要上鎖
synchronized (taskQueue) {
taskQueue.clear();
}

System.out.println("pool ends to destory ...");
}

private class WorkThread extends Thread{
private int taksId ;
private boolean isRuning = true;
private boolean isWaiting = false;



public WorkThread(int taskId){
this.taksId= taskId;
this.start();
}

public boolean isWaiting(){
return isWaiting;
}
// 如果任務進行中時,不能立刻終止線程,需要等待任務完成之後檢測到isRuning為false的時候,退出run()方法
public void stopThread(){
isRuning = false;
}

@Override
public void run() {
while(isRuning){
Upload temp = null;
//對任務隊列的操作要上鎖
synchronized (taskQueue) {
//任務隊列為空白,等待新的任務加入
while(isRuning&&taskQueue.isEmpty()){
try {
taskQueue.wait(20);
} catch (InterruptedException e) {
System.out.println("InterruptedException occre...");
e.printStackTrace();
}
}
if(isRuning)
temp = taskQueue.remove(0);
}
//當等待新任務加入時候,終止線程(調用stopThread函數)造成 temp = null
if(temp!=null){
System.out.println("task info: "+temp.getInfo()+ " is begining");
isWaiting = false;
temp.uploadPic();
isWaiting = true;
System.out.println("task info: "+temp.getInfo()+ " is finished");
}
}
}
}
}

  然後定義任務介面(Task):這裡我定義的是上傳圖片的功能介面(這裡用抽象類別或者介面隨你自己).

Upload

public abstract class Upload {
protected String info;
abstract boolean uploadPic();
public String getInfo(){
return info;
}
}

  然後定義具體任務類:我這裡簡單,讓它睡眠2s。當然你也可以定義很多實現Upload的任務類。

TaskUpload

public class TaskUpload extends Upload {

public TaskUpload(String info){
this.info = info;
}
public String getInfo(){
return info;
}
@Override
public boolean uploadPic() {
// TODO Auto-generated method stub
System.out.println(info+"sleep begin ....");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(info+"sleep end ....");
return false;
}
}

  最後,測試這個簡單的線程池:

public class ThreadPoolManagerTest {


public static void main(String[] args) {
// TODO Auto-generated method stub
Upload[] tasks = createBatchTask(7);
ThreadPoolManager pool = ThreadPoolManager.getInstance();
pool.BatchAddTask(tasks);
pool.destory();
}
private static Upload[] createBatchTask(int n){
Upload[] tasks = new TaskUpload[n];
for(int i = 0;i<n ;i++ ){
tasks[i] = new TaskUpload("task id is "+ i);
}
return tasks;
}
}

線程池技術適用範圍及應注意的問題

  線程池的應用範圍:

  1. 需要大量的線程來完成任務,且完成任務的時間比較短。 WEB伺服器完成網頁請求這樣的任務,使用線程池技術是非常合適的。因為單個任務小,而任務數量巨大,你可以想象一個熱門網站的點擊次數。 但對於長時間的任務,比如一個Telnet串連請求,線程池的優點就不明顯了。因為Telnet會話時間比線程的建立時間大多了。
  2. 對效能要求苛刻的應用,比如要求伺服器迅速相應客戶請求。
  3. 接受突發性的大量請求,但不至於使伺服器因此產生大量線程的應用。突發性大量客戶請求,在沒有線程池情況下,將產生大量線程,雖然理論上大部分作業系統線程數目最大值不是問題,短時間內產生大量線程可能使記憶體到達極限,並出現"OutOfMemory"的錯誤。

參考:   線程池的介紹及簡單實現

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.