最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java線程池的幾種實現(xiàn)方法及常見問題解答

 更新時間:2016年05月12日 11:08:40   投稿:jingxian  
下面小編就為大家?guī)硪黄狫ava線程池的幾種實現(xiàn)方法及常見問題解答。小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

工作中,經(jīng)常會涉及到線程。比如有些任務(wù),經(jīng)常會交與線程去異步執(zhí)行。抑或服務(wù)端程序為每個請求單獨(dú)建立一個線程處理任務(wù)。線程之外的,比如我們用的數(shù)據(jù)庫連接。這些創(chuàng)建銷毀或者打開關(guān)閉的操作,非常影響系統(tǒng)性能。所以,“池”的用處就凸顯出來了。

1. 為什么要使用線程池

在3.6.1節(jié)介紹的實現(xiàn)方式中,對每個客戶都分配一個新的工作線程。當(dāng)工作線程與客戶通信結(jié)束,這個線程就被銷毀。這種實現(xiàn)方式有以下不足之處:

•服務(wù)器創(chuàng)建和銷毀工作的開銷( 包括所花費(fèi)的時間和系統(tǒng)資源 )很大。這一項不用解釋,可以去查下"線程創(chuàng)建過程"。除了機(jī)器本身所做的工作,我們還要實例化,啟動,這些都需要占用堆棧資源。

•除了創(chuàng)建和銷毀線程的開銷之外,活動的線程也消耗系統(tǒng)資源。 這個應(yīng)該是對堆棧資源的消耗,猜測數(shù)據(jù)庫連接數(shù)設(shè)置一個合理的值,也有這個考慮。

•如果線程數(shù)目固定,并且每個線程都有很長的聲明周期,那么線程切換也是相對固定的。不同的操作系統(tǒng)有不同的切換周期,一般20ms左右。這里說的切換是在jvm以及底層操作系統(tǒng)的調(diào)度下,線程之間轉(zhuǎn)讓cpu的使用權(quán)。如果頻繁創(chuàng)建和銷毀線程,那么就將頻繁的切換線程,因為一個線程銷毀后,必然要讓出使用權(quán)給已經(jīng)就緒的線程,使該線程獲得運(yùn)行機(jī)會。在這種情況下,線程之間的切換就不在遵循系統(tǒng)的固定切換周期,切換線程的開銷甚至比創(chuàng)建和銷毀的開銷還要大。

相對來說,使用線程池,會預(yù)創(chuàng)建一些線程,它們不斷的從工作隊列中取出任務(wù),然后執(zhí)行該任務(wù)。當(dāng)工作線程執(zhí)行完一個任務(wù)后,就會繼續(xù)執(zhí)行工作隊列中的另一個任務(wù)。優(yōu)點如下:

•減少了創(chuàng)建和銷毀的次數(shù),每個工作線程都可以一直被重用,能執(zhí)行多個任務(wù)。

•可以根據(jù)系統(tǒng)的承載能力,方便的調(diào)整線程池中線程的數(shù)目,防止因為消耗過量的系統(tǒng)資源而導(dǎo)致系統(tǒng)崩潰。

2. 線程池的簡單實現(xiàn)

下面是自己寫的一個簡單的線程池,也是從Java網(wǎng)絡(luò)編程這本書上直接照著敲出來的

package thread;

import java.util.LinkedList;

/**
 * 線程池的實現(xiàn),根據(jù)常規(guī)線程池的長度,最大長度,隊列長度,我們可以增加數(shù)目限制實現(xiàn)
 * @author Han
 */
public class MyThreadPool extends ThreadGroup{
  //cpu 數(shù)量 ---Runtime.getRuntime().availableProcessors();
  //是否關(guān)閉
  private boolean isClosed = false;
  //隊列
  private LinkedList<Runnable> workQueue;
  //線程池id
  private static int threadPoolID;
  private int threadID;
  public MyThreadPool(int poolSize){
    super("MyThreadPool."+threadPoolID);
    threadPoolID++;
    setDaemon(true);
    workQueue = new LinkedList<Runnable>();
    for(int i = 0;i<poolSize;i++){
      new WorkThread().start();
    }
  }
  //這里可以換成ConcurrentLinkedQueue,就可以避免使用synchronized的效率問題
  public synchronized void execute(Runnable task){
    if(isClosed){
      throw new IllegalStateException("連接池已經(jīng)關(guān)閉...");
    }else{
      workQueue.add(task);
      notify();
    }
  }
  
  protected synchronized Runnable getTask() throws InterruptedException {
    while(workQueue.size() == 0){
      if(isClosed){
        return null;
      }
      wait();
    }
    return workQueue.removeFirst();
  }
  
  public synchronized void close(){
    if(!isClosed){
      isClosed = true;
      workQueue.clear();
      interrupt();
    }
  }
  
  public void join(){
    synchronized (this) {
      isClosed = true;
      notifyAll();
    }
    Thread[] threads = new Thread[activeCount()];
    int count = enumerate(threads);
    for(int i = 0;i<count;i++){
      try {
        threads[i].join();
      } catch (Exception e) {
      }
    }
  }
  
  class WorkThread extends Thread{
    public WorkThread(){
      super(MyThreadPool.this,"workThread"+(threadID++));
      System.out.println("create...");
    }
    @Override
    public void run() {
      while(!isInterrupted()){
        System.out.println("run..");
        Runnable task = null;
        try {
          //這是一個阻塞方法
          task = getTask();
          
        } catch (Exception e) {
          
        }
        if(task != null){
          task.run();
        }else{
          break;
        }
      }
    }
  }
}

該線程池主要定義了一個工作隊列和一些預(yù)創(chuàng)建的線程。只要調(diào)用execute方法,就可以向線程提交任務(wù)。

后面線程在沒有任務(wù)的時候,會阻塞在getTask(),直到有新任務(wù)進(jìn)來被喚醒。

join和close都可以用來關(guān)閉線程池。不同的是,join會把隊列中的任務(wù)執(zhí)行完,而close則立刻清空隊列,并且中斷所有的工作線程。close()中的interrupt()相當(dāng)于調(diào)用了ThreadGroup中包含子線程的各自的interrupt(),所以有線程處于wait或者sleep時,都會拋出InterruptException

測試類如下:

public class TestMyThreadPool {
  public static void main(String[] args) throws InterruptedException {
    MyThreadPool pool = new MyThreadPool(3);
    for(int i = 0;i<10;i++){
      pool.execute(new Runnable() {
        @Override
        public void run() {
          try {
            Thread.sleep(1000);
          } catch (InterruptedException e) {
          }
          System.out.println("working...");
        }
      });
    }
    pool.join();
    //pool.close();
  }
}

3. jdk類庫提供的線程池

java提供了很好的線程池實現(xiàn),比我們自己的實現(xiàn)要更加健壯以及高效,同時功能也更加強(qiáng)大。

類圖如下:

關(guān)于這類線程池,前輩們已經(jīng)有很好的講解。任意百度下java線程池,都有寫的非常詳細(xì)的例子和教程,這里就不再贅述。

4. spring注入線程池

在使用spring框架的時候,如果我們用java提供的方法來創(chuàng)建線程池,在多線程應(yīng)用中非常不方便管理,而且不符合我們使用spring的思想。(雖然spring可以通過靜態(tài)方法注入)

其實,Spring本身也提供了很好的線程池的實現(xiàn)。這個類叫做ThreadPoolTaskExecutor。

在spring中的配置如下:

<bean id="executorService" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
    <property name="corePoolSize" value="${threadpool.corePoolSize}" />
    <!-- 線程池維護(hù)線程的最少數(shù)量 -->
    <property name="keepAliveSeconds" value="${threadpool.keepAliveSeconds}" />
    <!-- 線程池維護(hù)線程所允許的空閑時間 -->
    <property name="maxPoolSize" value="${threadpool.maxPoolSize}" />
    <!-- 線程池維護(hù)線程的最大數(shù)量 -->
    <property name="queueCapacity" value="${threadpool.queueCapacity}" />
    <!-- 線程池所使用的緩沖隊列 -->
  </bean>

5. 使用線程池的注意事項

•死鎖

任何多線程程序都有死鎖的風(fēng)險,最簡單的情形是兩個線程AB,A持有鎖1,請求鎖2,B持有鎖2,請求鎖1。(這種情況在mysql的排他鎖也會出現(xiàn),不會數(shù)據(jù)庫會直接報錯提示)。線程池中還有另一種死鎖:假設(shè)線程池中的所有工作線程都在執(zhí)行各自任務(wù)時被阻塞,它們在等待某個任務(wù)A的執(zhí)行結(jié)果。而任務(wù)A卻處于隊列中,由于沒有空閑線程,一直無法得以執(zhí)行。這樣線程池的所有資源將一直阻塞下去,死鎖也就產(chǎn)生了。

•系統(tǒng)資源不足

 如果線程池中的線程數(shù)目非常多,這些線程會消耗包括內(nèi)存和其他系統(tǒng)資源在內(nèi)的大量資源,從而嚴(yán)重影響系統(tǒng)性能。

•并發(fā)錯誤

線程池的工作隊列依靠wait()和notify()方法來使工作線程及時取得任務(wù),但這兩個方法難以使用。如果代碼錯誤,可能會丟失通知,導(dǎo)致工作線程一直保持空閑的狀態(tài),無視工作隊列中需要處理的任務(wù)。因為最好使用一些比較成熟的線程池。

•線程泄漏

使用線程池的一個嚴(yán)重風(fēng)險是線程泄漏。對于工作線程數(shù)目固定的線程池,如果工作線程在執(zhí)行任務(wù)時拋出RuntimeException或Error,并且這些異?;蝈e誤沒有被捕獲,那么這個工作線程就異常終止,使線程池永久丟失了一個線程。(這一點太有意思)

另一種情況是,工作線程在執(zhí)行一個任務(wù)時被阻塞,如果等待用戶的輸入數(shù)據(jù),但是用戶一直不輸入數(shù)據(jù),導(dǎo)致這個線程一直被阻塞。這樣的工作線程名存實亡,它實際上不執(zhí)行任何任務(wù)了。如果線程池中的所有線程都處于這樣的狀態(tài),那么線程池就無法加入新的任務(wù)了。

•任務(wù)過載

當(dāng)工作線程隊列中有大量排隊等待執(zhí)行的任務(wù)時,這些任務(wù)本身可能會消耗太多的系統(tǒng)資源和引起資源缺乏。

綜上所述,使用線程池時,要遵循以下原則:

1. 如果任務(wù)A在執(zhí)行過程中需要同步等待任務(wù)B的執(zhí)行結(jié)果,那么任務(wù)A不適合加入到線程池的工作隊列中。如果把像任務(wù)A一樣的需要等待其他任務(wù)執(zhí)行結(jié)果的加入到隊列中,可能造成死鎖

2. 如果執(zhí)行某個任務(wù)時可能會阻塞,并且是長時間的阻塞,則應(yīng)該設(shè)定超時時間,避免工作線程永久的阻塞下去而導(dǎo)致線程泄漏。在服務(wù)器才程序中,當(dāng)線程等待客戶連接,或者等待客戶發(fā)送的數(shù)據(jù)時,都可能造成阻塞,可以通過以下方式設(shè)置時間:

調(diào)用ServerSocket的setSotimeout方法,設(shè)定等待客戶連接的超時時間。

對于每個與客戶連接的socket,調(diào)用該socket的setSoTImeout方法,設(shè)定等待客戶發(fā)送數(shù)據(jù)的超時時間。

3. 了解任務(wù)的特點,分析任務(wù)是執(zhí)行經(jīng)常會阻塞io操作,還是執(zhí)行一直不會阻塞的運(yùn)算操作。前者時斷時續(xù)的占用cpu,而后者具有更高的利用率。預(yù)計完成任務(wù)大概需要多長時間,是短時間任務(wù)還是長時間任務(wù),然后根據(jù)任務(wù)的特點,對任務(wù)進(jìn)行分類,然后把不同類型的任務(wù)加入到不同的線程池的工作隊列中,這樣就可以根據(jù)任務(wù)的特點,分配調(diào)整每個線程池

4. 調(diào)整線程池的大小。線程池的最佳大小主要取決于系統(tǒng)的可用cpu的數(shù)目,以及工作隊列中任務(wù)的特點。假如一個具有N個cpu的系統(tǒng)上只有一個工作隊列,并且其中全部是運(yùn)算性質(zhì)(不會阻塞)的任務(wù),那么當(dāng)線程池?fù)碛蠳或N+1個工作線程時,一般會獲得最大的cpu使用率。

如果工作隊列中包含會執(zhí)行IO操作并經(jīng)常阻塞的任務(wù),則要讓線程池的大小超過可用 cpu的數(shù)量,因為并不是所有的工作線程都一直在工作。選擇一個典型的任務(wù),然后估計在執(zhí)行這個任務(wù)的工程中,等待時間與實際占用cpu進(jìn)行運(yùn)算的時間的比例WT/ST。對于一個具有N個cpu的系統(tǒng),需要設(shè)置大約N*(1+WT/ST)個線程來保證cpu得到充分利用。

當(dāng)然,cpu利用率不是調(diào)整線程池過程中唯一要考慮的事項,隨著線程池工作數(shù)目的增長,還會碰到內(nèi)存或者其他資源的限制,如套接字,打開的文件句柄或數(shù)據(jù)庫連接數(shù)目等。要保證多線程消耗的系統(tǒng)資源在系統(tǒng)承受的范圍之內(nèi)。

5. 避免任務(wù)過載。服務(wù)器應(yīng)根據(jù)系統(tǒng)的承載能力,限制客戶并發(fā)連接的數(shù)目。當(dāng)客戶的連接超過了限制值,服務(wù)器可以拒絕連接,并進(jìn)行友好提示,或者限制隊列長度。

以上這篇Java線程池的幾種實現(xiàn)方法及常見問題解答就是小編分享給大家的全部內(nèi)容了,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • springboot接收json數(shù)據(jù)時,接收到空值問題

    springboot接收json數(shù)據(jù)時,接收到空值問題

    這篇文章主要介紹了springboot接收json數(shù)據(jù)時,接收到空值問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • Java程序生成exe可執(zhí)行文件詳細(xì)教程(圖文說明)

    Java程序生成exe可執(zhí)行文件詳細(xì)教程(圖文說明)

    這篇文章主要介紹了Java程序生成exe可執(zhí)行文件詳細(xì)教程,有需要的朋友可以參考一下
    2013-12-12
  • Java實現(xiàn)PDF轉(zhuǎn)圖片的三種方法

    Java實現(xiàn)PDF轉(zhuǎn)圖片的三種方法

    有些時候我們需要在項目中展示PDF,所以我們可以將PDF轉(zhuǎn)為圖片,然后已圖片的方式展示,效果很好,Java使用各種技術(shù)將pdf轉(zhuǎn)換成圖片格式,并且內(nèi)容不失幀,本文給大家介紹了三種方法實現(xiàn)PDF轉(zhuǎn)圖片的案例,需要的朋友可以參考下
    2023-10-10
  • MyBatis使用雪花ID的實現(xiàn)

    MyBatis使用雪花ID的實現(xiàn)

    本文主要介紹了MyBatis使用雪花ID的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-04-04
  • 學(xué)習(xí)Java之如何對時間進(jìn)行格式化

    學(xué)習(xí)Java之如何對時間進(jìn)行格式化

    當(dāng)我們在默認(rèn)情況下構(gòu)造出來的時間對象,它的時間格式并不適合我們閱讀,并且在開發(fā)時,pc端、Android端、iOS端等展示的時間格式可能也并不完全一樣,本文就從這幾個問題給大家介紹如何對時間進(jìn)行格式化,感興趣的同學(xué)可以借鑒一下
    2023-05-05
  • spring @Scheduled定時任務(wù)注解使用方法及注意事項小結(jié)

    spring @Scheduled定時任務(wù)注解使用方法及注意事項小結(jié)

    Spring的@Scheduled注解用于定時任務(wù)調(diào)度,默認(rèn)單線程依次執(zhí)行,可以通過配置多線程調(diào)度器或使用@Async注解實現(xiàn)并行執(zhí)行,常見參數(shù)包括cron、fixedRate、fixedDelay、initialDelay等,本文介紹spring @Scheduled定時任務(wù)注解使用方法,感興趣的朋友一起看看吧
    2025-02-02
  • 基于Java實現(xiàn)多線程下載并允許斷點續(xù)傳

    基于Java實現(xiàn)多線程下載并允許斷點續(xù)傳

    這篇文章主要介紹了基于Java實現(xiàn)多線程下載并允許斷點續(xù)傳,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-03-03
  • 利用java實現(xiàn)二維碼和背景圖的合并

    利用java實現(xiàn)二維碼和背景圖的合并

    本文介紹如何使用java代碼將自動生成的二維碼放入背景模板中,對于java學(xué)習(xí)者或許有幫助,一起來看看。
    2016-07-07
  • Java的MoreSuppliers工具類方法解析

    Java的MoreSuppliers工具類方法解析

    這篇文章主要介紹了Java的MoreSuppliers工具類方法解析,MoreSuppliers類是一個Java工具類,它提供了一些增強(qiáng)的Supplier函數(shù),使得Supplier執(zhí)行的結(jié)果可以被緩存,真正的調(diào)用只執(zhí)行一次,需要的朋友可以參考下
    2024-01-01
  • jmeter中json提取器如何提取多個參數(shù)值

    jmeter中json提取器如何提取多個參數(shù)值

    關(guān)于jmeter中的正則表達(dá)式及json提取器可以提取響應(yīng)值,但是實際可以需要上個接口的多個響應(yīng)值,本文就詳細(xì)的介紹一下如何使用,感興趣的可以了解一下
    2021-11-11

最新評論

石屏县| 碌曲县| 新乡市| 咸丰县| 肃北| 轮台县| 北辰区| 哈尔滨市| 揭阳市| 贵溪市| 新河县| 习水县| 九江县| 伊宁市| 天水市| 西充县| 连云港市| 武功县| 宁武县| 洛宁县| 桑日县| 新沂市| 昌邑市| 英德市| 阳朔县| 廉江市| 无锡市| 庄河市| 无为县| 武夷山市| 恩施市| 九龙坡区| 华阴市| 萝北县| 图木舒克市| 启东市| 赫章县| 淳安县| 建阳市| 枣强县| 邵阳市|