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