Java多線程的4種實現方式


Java有四種實現多線程的方式:

1).繼承Thread類

2).實現Runnable接口

3).實現Callable接口

4).使用線程池

前三種實現方式的代碼:

 1 public class MultiThread {
 2   //繼承Thread類
 3     static class MyThread1 extends Thread{
 4         @Override
 5         public void run() {
 6             System.out.println(Thread.currentThread().getName()+": extends Thread方式");
 7         }
 8     }
 9   //實現Runnable接口
10     static class MyThread2 implements Runnable{
11         @Override
12         public void run() {
13             System.out.println(Thread.currentThread().getName()+": implements Runnable方式");
14         }
15     }
16   //實現Callable接口
17     static class MyThread3 implements Callable<String>{
18         @Override
19         public String call() throws Exception {
20             return Thread.currentThread().getName()+": implements Callable<V>方式";
21         }
22     }
23 
24     public static void main(String[] args) throws Exception{
25         Thread thread1 = new MyThread1();
26         Thread thread2 = new Thread(new MyThread2());
27         FutureTask<String> futureTask = new FutureTask<>(new MyThread3());
28         Thread thread3 = new Thread(futureTask);
29 
30         thread1.start();
31         thread2.start();
32         thread3.start();
33         System.out.println(futureTask.get());
34     }
35 }
運行結果:
Thread-0: extends Thread方式 Thread-1: implements Runnable方式 Thread-2: implements Callable<V>方式 

line26,27行當傳入一個Runnable target參數給Thread后,Thread的run()方法就會調用target.run(),參考Thread類部分源代碼:

public class Thread implements Runnable {
    public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
  @Override
  public void run() {
   if (target != null) {
  target.run();
  }
  }
}

1.繼承Thread本質上也是實現Runnable接口。

2.實現Callable接口的任務線程能返回執行結果,而實現Runnable接口的線程不能返回結果

3.使用Callable的方式時候需要用FutureTask<>進行包裝(適配),FutureTask實現了Runnable和Future,通過future里面的get()方法來得到返回值。需要注意的是get()方法是阻塞的,就是說取不到值會使主線程一直等待。

4.生產中幾乎都會使用線程池。

線程池:用來控制運行的線程的數量,處理過程中將任務放入等待隊列,然后在線程創建后啟動這些任務。如果線程數量超過了最大數量(maximumPoolSize),超出數量的線程將會在等待隊列排隊等候。如果等待隊列已滿,再進來的任務就會按照拒絕策略拒絕。

線程池的特點:線程復用,控制最大並發數,管理線程。

線程池的優勢:

1.降低資源消耗,通過復用線程來降低創建和銷毀線程的消耗。

2.提高響應速度,當任務到達時不需要等待創建線程。

3.提高線程的可管理性,使用線程池可以進行統一的分配,監控和調優。

java自帶的線程池的實現:

     //固定數量線程的線程池
        ExecutorService threadPool1 = Executors.newFixedThreadPool(cpuNum);
        //一個線程的線程池
        ExecutorService threadPool2 = Executors.newSingleThreadExecutor();
        //多個線程的線程池
        ExecutorService threadPool3 = Executors.newCachedThreadPool();
        //Java8新特性
     ExecutorService threadPool3 = Executors.newWorkStealingPool();

上面幾線程池的底層都是ThreadPoolExecutor(),ThreadPoolExecutor是線程池的核心類。ThreadPoolExecutor的構造器最多有7個可配參數:

ThreadPoolExecutor的7個參數:

  • corePoolSize:核心池的大小,這個參數跟后面講述的線程池的實現原理有非常大的關系。在創建了線程池后,默認情況下,線程池中並沒有任何線程,而是等待有任務到來才創建線程去執行任務,除非調用了prestartAllCoreThreads()或者prestartCoreThread()方法,從這2個方法的名字就可以看出,是預創建線程的意思,即在沒有任務到來之前就創建corePoolSize個線程或者一個線程。默認情況下,在創建了線程池后,線程池中的線程數為0,當有任務來之后,就會創建一個線程去執行任務,當線程池中的線程數目達到corePoolSize后,就會把到達的任務放到緩存隊列當中;
  • maximumPoolSize:線程池最大線程數,這個參數也是一個非常重要的參數,它表示在線程池中最多能創建多少個線程;
  • keepAliveTime:表示線程沒有任務執行時最多保持多久時間會終止。默認情況下,只有當線程池中的線程數大於corePoolSize時,keepAliveTime才會起作用,直到線程池中的線程數不大於corePoolSize,即當線程池中的線程數大於corePoolSize時,如果一個線程空閑的時間達到keepAliveTime,則會終止,直到線程池中的線程數不超過corePoolSize。但是如果調用了allowCoreThreadTimeOut(boolean)方法,在線程池中的線程數不大於corePoolSize時,keepAliveTime參數也會起作用,直到線程池中的線程數為0;
  • unit:參數keepAliveTime的時間單位,有7種取值:
    TimeUnit.DAYS;//天
    TimeUnit.HOURS;//小時
    TimeUnit.MINUTES;//分鍾
    TimeUnit.SECONDS;//秒
    TimeUnit.MILLISECONDS;//毫秒
    TimeUnit.MICROSECONDS;//微妙
    TimeUnit.NANOSECONDS;//納秒
  • workQueue:一個阻塞隊列,用來存儲等待執行的任務,這個參數的選擇也很重要,會對線程池的運行過程產生重大影響,一般來說,這里的阻塞隊列有以下幾種選擇:ArrayBlockingQueue和PriorityBlockingQueue使用較少,一般使用LinkedBlockingQueue和Synchronous。線程池的排隊策略與BlockingQueue有關。
  • threadFactory:線程工廠,主要用來創建線程;
  • handler:表示當拒絕處理任務時的策略,有以下四種取值:
    ThreadPoolExecutor.AbortPolicy:丟棄任務並拋出RejectedExecutionException異常。 
    ThreadPoolExecutor.DiscardPolicy:也是丟棄任務,但是不拋出異常。 
    ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊列最前面的任務,然后重新嘗試執行任務(重復此過程)
    ThreadPoolExecutor.CallerRunsPolicy:由調用線程處理該任務 

線程池詳細原理可以參考:https://www.cnblogs.com/dolphin0520/p/3932921.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM