如何建立Java中的線程池

來源:互聯網
上載者:User
線程是Java的一大特性,它可以是給定的指令序列、給定的方法中定義的變數或者一些共用資料(類一級的變數)。在Java中每個線程有自己的堆棧和程式計數器(PC),其中堆棧是用來跟蹤線程的上下文(上下文是當線程執行到某處時,當前的局部變數的值),而程式計數器則用來跟蹤當前線程正在執行的指令。

在通常情況下,一個線程不能訪問另外一個線程的堆棧變數,而且這個線程必須處於如下狀態之一:

1.排隊狀態(Ready),在使用者建立了一個線程以後,這個線程不會立即運行。當線程中的方法start()被調用時,這個線程就會進行排隊狀態,等待發送器將它轉入運行狀態(Running)。當一個進程被執行後它也可以進行排隊狀態。如果發送器允許的話,通過調用方法yield()就可以將進程放入排隊狀態。

2.運行狀態(Running),當發送器將CPU的已耗用時間分配給一個線程,這個線程就進入了運行狀態開始運行。

3.等待狀態(Waiting),很多原因都可以導致線程處於等待狀態,例如線程執行過程中被暫停,或者是等待I/O請求的完成而進入等待狀態。

在Java中不同的線程具有不同的優先順序,高優先順序的線程可以安排在低優先順序線程之前完成。如果多個線程具有相同的優先順序,Java會在不同的線程之間切換運行。一個應用程式可以通過使用線程中的方法setPriority()來設定線程的優先順序,使用方法getPriority()來獲得一個線程的優先順序。

線程的生命週期

一個線程的的生命週期可以分成兩階段:生存(Alive)周期和死亡(Dead)周期,其中生存周期又包括運行狀態(Running)和等待狀態(Waiting)。當建立一個新線程後,這個線程就進入了排隊狀態(Ready),當線程中的方法start()被調用時,線程就進入生存周期,這時它的方法isAlive()始終返回真值,直至線程進入死亡狀態。

線程的實現

有兩種方法可以實現線程,一種是擴充java.lang.Thread類,另一種是通過java.lang.Runnable介面。

Thread類封裝了線程的行為。要建立一個線程,必須建立一個從Thread類擴充出的新類。由於在Thread類中方法run()沒有提供任何的操作,因此,在建立線程時使用者必須覆蓋方法run()來完成有用的工作。當線程中的方法start()被調用時,方法run()再被調用。下面的代碼就是通過擴充Thread類來實現線程:

import java.awt.*;
class Sample1{
public static void main(String[] args){
 Mythread test1=new Mythread(1);
 Mythread test2=new Mythread(2);
 test1.start();
 test2.start();
}
}
class Mythread  extends Thread {
int id;
Mythread(int i)
{ id=i;}
public void run() {
 int i=0;
 while(id+i==1){
  try {sleep(1000);
 } catch(InterruptedException e) {}
}
System.out.println(“The id  is ”+id);
}

通常當使用者希望一個類能運行在自己的線程中,同時也擴充其它某些類的特性時,就需要藉助運行Runnable介面來實現。Runnable介面只有一個方法run()。不論什麼時候建立了一個使用Runnable介面的類,都必須在類中編寫run()方法來覆蓋介面中的run()方法。例如下面的代碼就是通過Runnable介面實現的線程:

import java.awt.*;
import java.applet.Applet;
public class Bounce extends Applet implements Runnable{
static int r=30;
static int x=100;
static int y=30;
Thread t; 
public void init()
{
 t = new Thread(this);
 t.start();
}
public void run()
{
 int y1=+1; 
 int i=1;
 int sleeptime=10;
 while(true)
 {
  y+=(i*y);    
  if(y-r<i ||y+r>getSize().height)             
   y1*=-1;
  try{
   t.sleep(sleeptime);
  }catch(InterruptedException e){ }
 }
}
}

為什麼要使用線程池

在Java中,如果每當一個請求到達就建立一個新線程,開銷是相當大的。在實際使用中,每個請求建立新線程的伺服器在建立和銷毀線程上花費的時間和消耗的系統資源,甚至可能要比花在處理實際的使用者請求的時間和資源要多得多。除了建立和銷毀線程的開銷之外,活動的線程也需要消耗系統資源。如果在一個JVM裡建立太多的線程,可能會導致系統由於過度消耗記憶體或“切換過度”而導致系統資源不足。為了防止資源不足,伺服器應用程式需要一些辦法來限制任何給定時刻處理的請求數目,儘可能減少建立和銷毀線程的次數,特別是一些資源耗費比較大的線程的建立和銷毀,盡量利用已有對象來進行服務,這就是“池化資源”技術產生的原因。

線程池主要用來解決線程生命週期開銷問題和資源不足問題。通過對多個任務重用線程,線程建立的開銷就被分攤到了多個任務上了,而且由於在請求到達時線程已經存在,所以消除了線程建立所帶來的延遲。這樣,就可以立即為請求服務,使應用程式響應更快。另外,通過適當地調整線程池中的線程數目可以防止出現資源不足的情況。

建立一個線程池

一個比較簡單的線程池至少應包含線程池管理器、背景工作執行緒、任務隊列、任務介面等部分。其中線程池管理器(ThreadPool Manager)的作用是建立、銷毀並管理線程池,將背景工作執行緒放入線程池中;背景工作執行緒是一個可以迴圈執行任務的線程,在沒有任務時進行等待;任務隊列的作用是提供一種緩衝機制,將沒有處理的任務放在任務隊列中;任務介面是每個任務必須實現的介面,主要用來規定任務的入口、任務執行完後的收尾工作、任務的執行狀態等,背景工作執行緒通過該介面調度任務的執行。下面的代碼實現了建立一個線程池,以及從線程池中取出線程的操作:

public class ThreadPool
{  
private Stack threadpool = new Stack();
private int poolSize;
private int currSize=0;
public void setSize(int n)
{
 poolSize = n;
}
public void run()
{
 for(int i=0;i<poolSize;i++)
 {
  WorkThread workthread=new WorkThread();
  threadpool.push(workthread);
  currSize++;
 }
}
public  synchronized WorkThread  getworker( )
{
 if (threadpool.empty())
  system.out.println(“stack is empty”);
 else
 try{ return threadpool.pop();
 } catch (EmptyStackException e){}
}
}

線程池適合應用的場合

當一個Web伺服器接受到大量短小線程的請求時,使用線程池技術是非常合適的,它可以大大減少線程的建立和銷毀次數,提高伺服器的工作效率。但如果線程要求的已耗用時間比較長,此時線程的已耗用時間比建立時間要長得多,單靠減少建立時間對系統效率的提高不明顯,此時就不適合應用線程池技術,需要藉助其它的技術來提高伺服器的服務效率。

 

 

 

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.