線程是由操作系統來進行調度的
1、實現多線程的四種方式
-
繼承Thread類
Tread類本質上就是一個實現了Runnable接口的一個實例,代表一個線程的實例。啓動線程的唯一方法是通過Thread的類的start()實例方法。start()是一個本地方法,它將啓動一個新線程,並執行run()方法。
通過自己的類直接繼承Tread類,並重寫run()方法。
-
實現Runnable接口
因爲繼承是單繼承,所以當自己的類已經繼承了另一個類的時候,就無法使用extends Tread了,可以實現Runnable接口,並實現run()方法。使用時先實例化一個Tread,並傳入自己的對象實例,當執行Tread.start()的時候,Tread的run()方法就會調用目標對象的run()方法
public void run() { if (target != null) { target.run(); } }
-
實現callable接口,通過futureTask包裝器來創建Tread線程
public interface Callable<V> { V call() throws Exception; } public class SomeCallable<V> extends OtherClass implements Callable<V> { @Override public V call() throws Exception { return null; } } //第一種,使用Tread Callable<V> oneCallable = new SomeCallable<V>(); FutureTask<V> oneTask = new FutureTask<V>(oneCallable); Thread oneThread = new Thread(oneTask); oneThread.start(); //第二種,使用線程池 ExecutorService executor = Executors.newCachedThreadPool(); Callable<V> oneCallable = new SomeCallable<V>(); FutureTask<Integer> futureTask = new FutureTask<Integer>(oneCallable); executor.submit(futureTask); executor.shutdown();
-
使用ExecutorService、Callable、Future實現有返回結果的線程
public interface Future<V> { boolean cancel(boolean mayInterruptIfRunning); boolean isCancelled();//表示任務是否被取消成功,如果任務正常完成前被取消成功,則返回true boolean isDone();//表示任務是否已經完成,若任務完成,返回true V get() throws InterruptedException, ExecutionException;//會產生阻塞,直到任務執行完畢 V get(long timeout, TimeUnit unit)//指定時間內沒獲取到結果,直接返回null throws InterruptedException, ExecutionException, TimeoutException; } // 創建一個線程池 ExecutorService pool = Executors.newFixedThreadPool(5); // 創建多個有返回值的任務 List<Future> list = new ArrayList<Future>(); for (int i = 0; i < 5; i++) { Callable c = new MyCallable(i + " "); // 執行任務並獲取Future對象 Future f = pool.submit(c); // System.out.println(">>>" + f.get().toString()); list.add(f); } // 關閉線程池 pool.shutdown(); // 獲取所有併發任務的運行結果 for (Future f : list) { // 從Future對象上獲取任務的返回值,並輸出到控制檯 System.out.println(">>>" + f.get().toString()); }
2、線程池
-
java.uitl.concurrent.ThreadPoolExecutor類是線程池最核心的類
//構造函數 public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler);
-
corePoolSize:核心池大小,當線程池中的線程數目達到corePoolSize大小後,就會把任務放到緩存隊列
-
maximumPoolSize:線程池的最大線程數,表示線程池最多能創建多少線程
當一個新的任務被提交到線程池的時候,1>,先判斷核心池是否已滿,未滿則創建一個新的線程來執行該任務;滿了則去判斷任務隊列是否已滿,未滿則將新提交的任務存儲在工作隊列中;滿了則去判斷最大線程數是否已滿,未滿則創建一個新的工作線程去執行任務;滿了則交給飽和策略來處理這個任務。如果線程池中的線程數大於核心池大小,則當某個線程空閒時間超過keepAliveTime時,該線程會被終止
一般核心線程池容量很小,最大容量比較大,當核心池滿了以後,會先將任務放在隊列中,然後判斷是否達到了最大線程數,沒有達到就創建線程執行;達到以後,任務就暫存在任務隊列中,直到任務隊列已滿,執行拒絕策略。
-
keepAliveTime:默認情況下,只有當線程池中的線程數量大於核心池時,這個參數纔會起作用
-
unit :是keepAliveTime的時間單位
-
workQueue:是一個阻塞隊列,用來存儲等待執行的任務
-
threadFactory:線程工廠,主要用來創建線程
-
handler:表示當拒絕任務處理時的策略
- AbortPolicy:丟棄任務並拋出RejectedExecutionException異常
- DiscardPolicy:丟棄任務,但不拋出異常
- DiscardOldestPolicy:丟棄隊列最前面的任務,然後重新嘗試執行任務(重複此過程)
- CallerRunsPolicy:由調用線程處理該任務
-
-
ThreadPoolExecutor有幾個重要的方法:
- execute():向線程池提交一個任務,交由線程池去執行
- submit():也是向線程池提交任務,和execute()的區別是submit()能夠返回任務的執行結果,實際上調用的還是execute(),只不過利用Future()來獲取任務的執行結果
- shutdown():關閉線程池,此時線程池不能夠接受新的任務,他會等待所有任務執行完畢
- shutdownNow():關閉線程池,此時線程池不能接受新的任務,並且嘗試終止正在進行的任務
-
線程池的狀態:
volatile int runState; static final int RUNNING = 0; static final int SHUTDOWN = 1; static final int STOP = 2; static final int TERMINATED = 3;
runState:表示當前線程池的狀態,使用volatile修飾來保證線程之前的可見性
-
線程池中的線程初始化
默認情況下,創建線程池之後,線程池中是沒有線程的,需要提交任務之後纔會創建線程
在實際中如果需要線程池創建之後 立即創建線程,可以通過兩個方法來實現:
- prestartCoreThread():初始化一個核心線程
- prestartAllCoreThreads():初始化核心池數量的線程
-
任務緩存隊列及排隊策略
workQueue的類型爲BlockingQueue,通常有三種類型:
- ArrayBlockingQueue:基於數組的先進先出隊列,此隊列創建時必須指定大小
- LinkedBlockingQueue:基於鏈表的阻塞隊列,如果創建時沒有指定此隊列的大小,默認爲Integer.MAX_VALUE;
- SynchronousQueue:這個隊列比較特殊,不會保存提交的任務,而是直接新建一個線程來執行新來的任務
-
具體使用
Executors.newCachedThreadPool(); //創建一個緩衝池,緩衝池容量大小爲Integer.MAX_VALUE Executors.newSingleThreadExecutor(); //創建容量爲1的緩衝池 Executors.newFixedThreadPool(int); //創建固定容量大小的緩衝池
具體實現:
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); } public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); } public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
從它們的具體實現來看,它們實際上也是調用了ThreadPoolExecutor,只不過參數都已配置好了。