1. 程式人生 > >多線程實現方式及區別

多線程實現方式及區別

sync ted 缺點 完全 eth 最大 trac kqueue urn

為什麽要用線程池?

  • 單獨創建線程的缺點: 

  a. 每次new Thread新建對象性能差。
  b. 線程缺乏統一管理,可能無限制新建線程,相互之間競爭,及可能占用過多系統資源導致死機或oom。
  c. 缺乏更多功能,如定時執行、定期執行、線程中斷。

  • 創建線程池的優點: 

  a. 重用存在的線程,減少對象創建、消亡的開銷,性能佳。
  b. 可有效控制最大並發線程數,提高系統資源的使用率,同時避免過多資源競爭,避免堵塞。
  c. 提供定時執行、定期執行、單線程、並發數控制,指定隊列大小,失敗策略等功能,可以根據我們項目的需要自定義創建不同功能的線程池。

兩種創建方式

  • Executors創建方式 
  • Executors.newCachedThreadPool()
public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
}

  示例:

package test;  
import java.util.concurrent.ExecutorService;  
import java.util.concurrent.Executors;  
public class ThreadPoolExecutorTest {  
   public static void main(String[] args) {  
      ExecutorService cachedThreadPool = Executors.newCachedThreadPool();  
        for (int
i = 0; i < 10; i++) {         final int index = i;         try {           Thread.sleep(index * 1000);         } catch (InterruptedException e) {           e.printStackTrace();          }          cachedThreadPool.execute(new Runnable() {             public void run() {             System.out.println(index);            }         });        }   } }

  說明:創建一個可緩存線程池,應用中存在的線程數可以無限大。當提交任務速度高於線程池中任務處理速度時,緩存線程池會不斷的創建線程,線程數完全取決於jvm可以創建的線程數,直到資源耗盡會拋出oom異常;如果在執行第二個任務時,第一個任務已經完成,那麽會復用第一個任務的線程執行第二個任務,如果一個線程超過60秒沒有被使用,就會被線程池回收。所以大家要慎用此方法,適用於提交短期的異步小程序,以及負載較輕的服務器。

  • Executors.newFixedThreadPool(2)
public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
}

  示例:

  

技術分享圖片
package test;  
import java.util.concurrent.ExecutorService;  
import java.util.concurrent.Executors;  
public class ThreadPoolExecutorTest {  
 public static void main(String[] args) {  
  ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);  
  for (int i = 0; i < 10; i++) {  
   final int index = i;  
   fixedThreadPool.execute(new Runnable() {  
    public void run() {  
     try {  
      System.out.println(index);  
      Thread.sleep(2000);  
     } catch (InterruptedException e) {  
      e.printStackTrace();  
     }  
    }  
   });  
  }  
 }  
}  
View Code

  說明:創建一個線程數固定的線程池,可以根據系統的資源進行設置,如:Runtime.getRuntime().availableProcessors()。線程不會被回收,除非調用shutdown()方法才會回收,如果某個線程因為執行異常而結束,那麽線程池會補充一個新線程。此方法可控制線程最大並發數,超出線程數據的任務會放到隊列中,但是從源碼中可以看到此隊列為無界隊列,所以如果我們一次查出的數據過多很有可能會導致oom異常,因為隊列會無限擴充。使用於為了滿足資源管理需求而需要限制當前線程數量的場合,使用於負載比較重的服務器但是要註意控制任務數量。

  • Executors.newScheduledThreadPool(2)

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
  return new ScheduledThreadPoolExecutor(corePoolSize);
}

public ScheduledThreadPoolExecutor(int corePoolSize) {
  super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,new DelayedWorkQueue());
}

  說明:計劃型線程池,可以設置固定時間的延時或者定期執行任務,多數情況下可用來替代Timer類,同樣是看線程池中有沒有空閑線程,如果有,直接拿來使用,如果沒有,則新建線程加入池。使用的是 DelayedWorkQueue 作為等待隊列,這中類型的隊列會保證只有到了指定的延時時間,才會執行任務

  • Executors.newSingleThreadExecutor();
public static ExecutorService newSingleThreadExecutor() {
  return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()));
}

  說明:創建一個單線程的線程池,它只會用唯一的工作線程來執行任務,也就相當於單線程串行執行任務。如果這個唯一的線程因為異常結束,那麽會有一個新的線程來替代它。保證所有任務按照指定順序(FIFO, LIFO, 優先級)執行。使用 LinkedBlockingQueue 作為等待隊列。

  

  • Executors.newSingleThreadScheduledExecutor();

   

  說明:創建一個單例線程池,定期或延時執行任務。

  • Executors.newWorkStealingPool(3);   
public static ExecutorService newWorkStealingPool(int parallelism) {
        return new ForkJoinPool
            (parallelism,
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
 }

  說明:創建一個帶並行級別的線程池,並行級別決定了同一時刻最多有多少個線程在執行,如不穿如並行級別參數,將默認為當前系統的CPU個數。

  • ThreadPoolExecutor創建方式

多線程實現方式及區別