您好,登錄后才能下訂單哦!
本文小編為大家詳細介紹“Java多線程怎么理解”,內容詳細,步驟清晰,細節處理妥當,希望這篇“Java多線程怎么理解”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學習新知識吧。
總體來說,線程池有如下的優勢:
(1)降低資源消耗。通過重復利用已創建的線程降低線程創建和銷毀造成的消耗。
(2)提高響應速度。當任務到達時,任務可以不需要等到線程創建就能立即執行。
(3)提高線程的可管理性。線程是稀缺資源,如果無限制的創建,不僅會消耗系統資源,還會降低系統的穩定性,使用線程池可以進行統一的分配,調優和監控。
線程池的真正實現類是 ThreadPoolExecutor,其構造方法有如下4種:
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler); } public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, defaultHandler); } public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), handler); } public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0) throw new IllegalArgumentException(); if (workQueue == null || threadFactory == null || handler == null) throw new NullPointerException(); this.corePoolSize = corePoolSize; this.maximumPoolSize = maximumPoolSize; this.workQueue = workQueue; this.keepAliveTime = unit.toNanos(keepAliveTime); this.threadFactory = threadFactory; this.handler = handler; }
可以看到,其需要如下幾個參數:
corePoolSize(必需):核心線程數。默認情況下,核心線程會一直存活,但是當將 allowCoreThreadTimeout 設置為 true 時,核心線程也會超時回收。
maximumPoolSize(必需):線程池所能容納的最大線程數。當活躍線程數達到該數值后,后續的新任務將會阻塞。
keepAliveTime(必需):線程閑置超時時長。如果超過該時長,非核心線程就會被回收。如果將 allowCoreThreadTimeout 設置為 true 時,核心線程也會超時回收。
unit(必需):指定 keepAliveTime 參數的時間單位。常用的有:TimeUnit.MILLISECONDS(毫秒)、TimeUnit.SECONDS(秒)、TimeUnit.MINUTES(分)。
workQueue(必需):任務隊列。通過線程池的 execute() 方法提交的 Runnable 對象將存儲在該參數中。其采用阻塞隊列實現。
threadFactory(可選):線程工廠。用于指定為線程池創建新線程的方式。
線程池的使用流程如下:
// 創建線程池 ThreadPoolExecutor threadPool = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory); // 向線程池提交任務 threadPool.execute(new Runnable() { @Override public void run() { ... // 線程執行的任務 } }); // 關閉線程池 threadPool.shutdown(); // 設置線程池的狀態為SHUTDOWN,然后中斷所有沒有正在執行任務的線程 threadPool.shutdownNow(); // 設置線程池的狀態為 STOP,然后嘗試停止所有的正在執行或暫停任務的線程,并返回等待執行任務的列表
下面來描述一下線程池工作的原理,同時對上面的參數有一個更深的了解。其工作原理流程圖如下:
通過上圖,相信大家已經對所有參數有個了解了。下面再對任務隊列、線程工廠和拒絕策略做更多的說明。
任務隊列是基于阻塞隊列實現的,即采用生產者消費者模式,在 Java 中需要實現 BlockingQueue 接口。但 Java 已經為我們提供了 7 種阻塞隊列的實現:
ArrayBlockingQueue:一個由數組結構組成的有界阻塞隊列(數組結構可配合指針實現一個環形隊列)。
LinkedBlockingQueue: 一個由鏈表結構組成的有界阻塞隊列,在未指明容量時,容量默認為 Integer.MAX_VALUE。
PriorityBlockingQueue: 一個支持優先級排序的無界阻塞隊列,對元素沒有要求,可以實現 Comparable 接口也可以提供 Comparator 來對隊列中的元素進行比較。跟時間沒有任何關系,僅僅是按照優先級取任務。
DelayQueue:類似于PriorityBlockingQueue,是二叉堆實現的無界優先級阻塞隊列。要求元素都實現 Delayed 接口,通過執行時延從隊列中提取任務,時間沒到任務取不出來。
SynchronousQueue: 一個不存儲元素的阻塞隊列,消費者線程調用 take() 方法的時候就會發生阻塞,直到有一個生產者線程生產了一個元素,消費者線程就可以拿到這個元素并返回;生產者線程調用 put() 方法的時候也會發生阻塞,直到有一個消費者線程消費了一個元素,生產者才會返回。
LinkedBlockingDeque: 使用雙向隊列實現的有界雙端阻塞隊列。雙端意味著可以像普通隊列一樣 FIFO(先進先出),也可以像棧一樣 FILO(先進后出)。
LinkedTransferQueue: 它是ConcurrentLinkedQueue、LinkedBlockingQueue 和 SynchronousQueue 的結合體,但是把它用在 ThreadPoolExecutor 中,和 LinkedBlockingQueue 行為一致,但是是無界的阻塞隊列。
注意有界隊列和無界隊列的區別:如果使用有界隊列,當隊列飽和時并超過最大線程數時就會執行拒絕策略;而如果使用無界隊列,因為任務隊列永遠都可以添加任務,所以設置 maximumPoolSize 沒有任何意義。
線程工廠指定創建線程的方式,需要實現 ThreadFactory 接口,并實現 newThread(Runnable r) 方法。該參數可以不用指定,Executors 框架已經為我們實現了一個默認的線程工廠:
/** * The default thread factory. */ private static class DefaultThreadFactory implements ThreadFactory { private static final AtomicInteger poolNumber = new AtomicInteger(1); private final ThreadGroup group; private final AtomicInteger threadNumber = new AtomicInteger(1); private final String namePrefix; DefaultThreadFactory() { SecurityManager s = System.getSecurityManager(); group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup(); namePrefix = "pool-" + poolNumber.getAndIncrement() + "-thread-"; } public Thread newThread(Runnable r) { Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0); if (t.isDaemon()) t.setDaemon(false); if (t.getPriority() != Thread.NORM_PRIORITY) t.setPriority(Thread.NORM_PRIORITY); return t; } }
當線程池的線程數達到最大線程數時,需要執行拒絕策略。拒絕策略需要實現 RejectedExecutionHandler 接口,并實現 rejectedExecution(Runnable r, ThreadPoolExecutor executor) 方法。不過 Executors 框架已經為我們實現了 4 種拒絕策略:
AbortPolicy(默認):丟棄任務并拋出 RejectedExecutionException 異常。
CallerRunsPolicy:由調用線程處理該任務。
DiscardPolicy:丟棄任務,但是不拋出異常。可以配合這種模式進行自定義的處理方式。
DiscardOldestPolicy:丟棄隊列最早的未處理任務,然后重新嘗試執行任務。
嫌上面使用線程池的方法太麻煩?其實Executors已經為我們封裝好了 4 種常見的功能線程池,如下:
定長線程池(FixedThreadPool)
定時線程池(ScheduledThreadPool )
可緩存線程池(CachedThreadPool)
單線程化線程池(SingleThreadExecutor)
創建方法的源碼:
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); } public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), threadFactory); }
特點:只有核心線程,線程數量固定,執行完立即回收,任務隊列為鏈表結構的有界隊列。
應用場景:控制線程最大并發數。
使用示例:
// 1. 創建定長線程池對象 & 設置線程池線程數量固定為3 ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3); // 2. 創建好Runnable類線程對象 & 需執行的任務 Runnable task =new Runnable(){ public void run() { System.out.println("執行任務啦"); } }; // 3. 向線程池提交任務 fixedThreadPool.execute(task);
創建方法的源碼:
private static final long DEFAULT_KEEPALIVE_MILLIS = 10L; public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { return new ScheduledThreadPoolExecutor(corePoolSize); } public ScheduledThreadPoolExecutor(int corePoolSize) { super(corePoolSize, Integer.MAX_VALUE, DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS, new DelayedWorkQueue()); } public static ScheduledExecutorService newScheduledThreadPool( int corePoolSize, ThreadFactory threadFactory) { return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory); } public ScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory) { super(corePoolSize, Integer.MAX_VALUE, DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS, new DelayedWorkQueue(), threadFactory); }
特點:核心線程數量固定,非核心線程數量無限,執行完閑置 10ms 后回收,任務隊列為延時阻塞隊列。
應用場景:執行定時或周期性的任務。
使用示例:
// 1. 創建 定時線程池對象 & 設置線程池線程數量固定為5 ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5); // 2. 創建好Runnable類線程對象 & 需執行的任務 Runnable task =new Runnable(){ public void run() { System.out.println("執行任務啦"); } }; // 3. 向線程池提交任務 scheduledThreadPool.schedule(task, 1, TimeUnit.SECONDS); // 延遲1s后執行任務 scheduledThreadPool.scheduleAtFixedRate(task,10,1000,TimeUnit.MILLISECONDS);// 延遲10ms后、每隔1000ms執行任務
創建方法的源碼:
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); } public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), threadFactory); }
特點:無核心線程,非核心線程數量無限,執行完閑置 60s 后回收,任務隊列為不存儲元素的阻塞隊列。
應用場景:執行大量、耗時少的任務。
使用示例:
// 1. 創建可緩存線程池對象 ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); // 2. 創建好Runnable類線程對象 & 需執行的任務 Runnable task =new Runnable(){ public void run() { System.out.println("執行任務啦"); } }; // 3. 向線程池提交任務 cachedThreadPool.execute(task);
創建方法的源碼:
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); } public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), threadFactory)); }
特點:只有 1 個核心線程,無非核心線程,執行完立即回收,任務隊列為鏈表結構的有界隊列。
應用場景:不適合并發但可能引起 IO 阻塞性及影響 UI 線程響應的操作,如數據庫操作、文件操作等
使用示例:
// 1. 創建單線程化線程池 ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor(); // 2. 創建好Runnable類線程對象 & 需執行的任務 Runnable task =new Runnable(){ public void run() { System.out.println("執行任務啦"); } }; // 3. 向線程池提交任務 singleThreadExecutor.execute(task);
讀到這里,這篇“Java多線程怎么理解”文章已經介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領會,如果想了解更多相關內容的文章,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。