徹底搞懂Java多線程(三)
線程的缺點:
1.線程的創(chuàng)建它會開辟本地方法棧、JVM棧、程序計數(shù)器私有的內(nèi)存,同時消耗的時候需要銷毀以上三個區(qū)域,因此頻繁的創(chuàng)建和銷毀線程比較消耗系統(tǒng)的資源。
2.在任務(wù)量遠遠大于線程可以處理的任務(wù)量的時候,不能很好的拒絕任務(wù)。
所以就有了線程池:
使用池化的而技術(shù)來管理和使用線程。
線程池的優(yōu)點1.可以避免頻繁的創(chuàng)建和銷毀線程
2.可以更好的管理線程的個數(shù)和資源的個數(shù)。
3.線程池擁有更多的功能,比如線程池可以進行定時任務(wù)的執(zhí)行。
4.線程池可以更友好的拒絕不能處理的任務(wù)。
線程池的6種創(chuàng)建方式一共有7種創(chuàng)建方式
創(chuàng)建方式一:
創(chuàng)建固定個數(shù)的線程池:
package ThreadPoolDemo;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;/** * user:ypc; * date:2021-06-13; * time: 10:24; */public class ThreadPoolDemo1 { public static void main(String[] args) {//創(chuàng)建一個固定個數(shù)的線程池ExecutorService executorService = Executors.newFixedThreadPool(10);//執(zhí)行任務(wù)for (int i = 0; i < 10; i++) { executorService.execute(new Runnable() {@Overridepublic void run() { System.out.println('線程名' + Thread.currentThread().getName());} });} }}
那么如果執(zhí)行次數(shù)大于10次呢?
線程池不會創(chuàng)建新的線程,它會復(fù)用之前的線程。
那么如果只執(zhí)行兩個任務(wù)呢?它創(chuàng)建了是10個線程還是兩個線程呢?
我們可以使用Jconsole來看一看:
結(jié)果是只有2個線程被創(chuàng)建。
創(chuàng)建方式二:
創(chuàng)建帶有緩存的線程池:
適用于短期有大量的任務(wù)的時候使用
public class ThreadPoolDemo2 { public static void main(String[] args) {//創(chuàng)建帶緩存的線程池ExecutorService executorService = Executors.newCachedThreadPool();for (int i = 0; i < 100; i++) { executorService.execute(new Runnable() {@Overridepublic void run() { System.out.println(Thread.currentThread().getName());} });} }}
方式三:
創(chuàng)建執(zhí)行定時任務(wù)的線程池
package ThreadPoolDemo;import java.util.Date;import java.util.concurrent.Executors;import java.util.concurrent.ScheduledExecutorService;import java.util.concurrent.TimeUnit;/** * user:ypc; * date:2021-06-13; * time: 11:32; */public class ThreadPoolDemo3 { public static void main(String[] args) {ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);System.out.println('執(zhí)行定時任務(wù)前的時間:' + new Date());scheduledExecutorService.scheduleAtFixedRate(new Runnable() { @Override public void run() {System.out.println('執(zhí)行任務(wù)的時間:' + new Date()); }},1,2, TimeUnit.SECONDS); }}
執(zhí)行任務(wù)的四個參數(shù)的意義:
參數(shù)1:延遲執(zhí)行的任務(wù)
參數(shù)2:延遲一段時間后執(zhí)行
參數(shù)3:定時任務(wù)執(zhí)行的頻率
參數(shù)4:配合前兩個參數(shù)使用,是2、3參數(shù)的時間單位
還有兩種執(zhí)行的方法:
只會執(zhí)行一次的方法:
第三種的執(zhí)行方式:
那么這種的執(zhí)行方式和第一種的執(zhí)行方式有什么區(qū)別呢?
當(dāng)在兩種執(zhí)行的方式中分別加上sleep()之后:
方式一:
方式三:
結(jié)論很明顯了:
第一種方式是以上一個任務(wù)的開始時間+定時的時間作為當(dāng)前任務(wù)的開始時間
第三種方式是以上一個任務(wù)的結(jié)束時間來作為當(dāng)前任務(wù)的開始時間。
創(chuàng)建方式四:
package ThreadPoolDemo;import java.util.Date;import java.util.concurrent.Executors;import java.util.concurrent.ScheduledExecutorService;import java.util.concurrent.TimeUnit;/** * user:ypc; * date:2021-06-13; * time: 12:38; */public class ThreadPoolDemo4 { public static void main(String[] args) {//創(chuàng)建單個執(zhí)行任務(wù)的線程池ScheduledExecutorService scheduledExecutorService= Executors.newSingleThreadScheduledExecutor();System.out.println('執(zhí)行任務(wù)之前' + new Date());scheduledExecutorService.scheduleWithFixedDelay(new Runnable() { @Override public void run() {System.out.println('我是SingleThreadSchedule'+ new Date()); }},3,1, TimeUnit.SECONDS); }}
創(chuàng)建方式五:
創(chuàng)建單個線程的線程池
package ThreadPoolDemo;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;/** * user:ypc; * date:2021-06-13; * time: 12:55; */public class ThreadPoolDemo5 { public static void main(String[] args) {//創(chuàng)建單個線程的線程池ExecutorService executorService = Executors.newSingleThreadExecutor();for (int i = 0; i < 20; i++) { executorService.execute(new Runnable() {@Overridepublic void run() { System.out.println('線程名 ' + Thread.currentThread().getName());} });} }}
1.可以避免頻繁創(chuàng)建和銷毀線程所帶來的性能的開銷
2.它有任務(wù)隊列,可以存儲多余的任務(wù)
3.可以更好的管理任務(wù)
4.當(dāng)有大量的任務(wù)不能處理的時候,可以友好的執(zhí)行拒絕策略
創(chuàng)建方式六:
創(chuàng)建異步線程池根據(jù)當(dāng)前CPU來創(chuàng)建對應(yīng)個數(shù)的線程池
package ThreadPoolDemo;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;/** * user:ypc; * date:2021-06-13; * time: 13:12; */public class ThreadPoolDemo6 { public static void main(String[] args) {ExecutorService executorService = Executors.newWorkStealingPool();for (int i = 0; i < 10; i++) { executorService.execute(new Runnable() {@Overridepublic void run() { System.out.println('線程名' + Thread.currentThread().getName());} });} }}
運行結(jié)果為什么什么都沒有呢?
看下面的異步與同步的區(qū)別就知道了。
加上這個
就可以輸出結(jié)果了
前六種的創(chuàng)建方式有什么問題呢?
1.線程的數(shù)量不可控(比如帶緩存的線程池)
2.工作任務(wù)量不可控(默認(rèn)的任務(wù)隊列的大小時Integer.MAX_VALUE),任務(wù)比較大肯會導(dǎo)致內(nèi)存的溢出。
所以就可以使用下面的創(chuàng)建線程池的方式了:
package ThreadPoolDemo;import java.util.concurrent.LinkedBlockingDeque;import java.util.concurrent.ThreadFactory;import java.util.concurrent.ThreadPoolExecutor;import java.util.concurrent.TimeUnit;/** * user:ypc; * date:2021-06-13; * time: 15:05; */public class ThreadPoolDemo7 { private static int threadId = 0; public static void main(String[] args) {ThreadFactory threadFactory = new ThreadFactory() { @Override public Thread newThread(Runnable r) {Thread thread = new Thread(r);thread.setName('我是threadPool-' + ++threadId);return thread; }};ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3, 3, 100,TimeUnit.MILLISECONDS, new LinkedBlockingDeque<>(12),threadFactory, new ThreadPoolExecutor.AbortPolicy());for (int i = 0; i < 15; i++) { threadPoolExecutor.execute(new Runnable() {@Overridepublic void run() { System.out.println(Thread.currentThread().getName());} });} }}
參數(shù)說明:
注意事項:最大的線程數(shù)要大于等于核心的線程數(shù)
五種拒絕策略
為什么拒絕策略可以舍棄最新的任務(wù)或者最舊的任務(wù)呢?
因為LinkedBlockingDeque時FIFO的。
第五種:自定義的拒絕策略
package ThreadPoolDemo;import java.util.concurrent.*;/** * user:ypc; * date:2021-06-13; * time: 16:58; */public class ThreadPoolDemo9 { public static void main(String[] args) throws ExecutionException, InterruptedException {ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3, 4, 100,TimeUnit.MILLISECONDS, new LinkedBlockingDeque<>(10), new ThreadPoolExecutor.DiscardOldestPolicy());//線程池的執(zhí)行方式一threadPoolExecutor.execute(new Runnable() { @Override public void run() {System.out.println('使用了execute()執(zhí)行了線程池'); }});//線程池的執(zhí)行方式二Future<String> futureTask =threadPoolExecutor.submit(new Callable<String>() { @Override public String call() throws Exception {return '使用submit(new Callable<>())執(zhí)行了線程池'; }});System.out.println(futureTask.get()); }}
無返回值的執(zhí)行方式
有返回值的執(zhí)行方式
當(dāng)任務(wù)量小于核心線程數(shù)的時候,ThreadPoolExecutor會創(chuàng)建線程來執(zhí)行任務(wù)
當(dāng)任務(wù)量大于核心的線程數(shù)的時候,并且沒有空閑的線程時候,且當(dāng)線程池的線程數(shù)小于最大線程數(shù)的時候,此時會將任務(wù)存
放到任務(wù)隊列中
如果任務(wù)隊列也被存滿了,且最大線程數(shù)大于線程池的線程數(shù)的時候,會創(chuàng)建新的線程來執(zhí)行任務(wù)。
如果線程池的線程數(shù)等于最大的線程數(shù),并且任務(wù)隊列也已經(jīng)滿了,就會執(zhí)行拒絕策略。👇
shutdown()
線程池的任務(wù)會執(zhí)行完
shutdownNow()
立即終止線程池,線程池的任務(wù)不會執(zhí)行完
線程池的狀態(tài)多線程并發(fā)時,多個線程同時請求同一個資源,必然導(dǎo)致此資源的數(shù)據(jù)不安全,A線程修改了B線程的處理的數(shù)據(jù),而B線程又修改了A線程處理的數(shù)理。顯然這是由于全局資源造成的,有時為了解決此問題,優(yōu)先考慮使用局部變量,退而求其次使用同步代碼塊,出于這樣的安全考慮就必須犧牲系統(tǒng)處理性能,加在多線程并發(fā)時資源掙奪最激烈的地方,這就實現(xiàn)了線程的同步機制
同步
A線程要請求某個資源,但是此資源正在被B線程使用中,因為同步機制存在,A線程請求不到,怎么辦,A線程只能等待下去
異步
A線程要請求某個資源,但是此資源正在被B線程使用中,因為沒有同步機制存在,A線程仍然請求的到,A線程無需等待同步的方式:
1.發(fā)送請求
2.等待執(zhí)行完成
3.有結(jié)果的返回
異步的方式
1.發(fā)請求
2.執(zhí)行完成
3.另一個線程異步處理
4.處理完成之后返回回調(diào)結(jié)果
顯然,同步最最安全,最保險的。而異步不安全,容易導(dǎo)致死鎖,這樣一個線程死掉就會導(dǎo)致整個進程崩潰,使用異步的機制,性能會有所提升
線程工廠設(shè)想這樣一種場景,我們需要一個線程池,并且對于線程池中的線程對象,賦予統(tǒng)一的線程優(yōu)先級、統(tǒng)一的名稱、甚至進行統(tǒng)一的業(yè)務(wù)處理或和業(yè)務(wù)方面的初始化工作,這時工廠方法就是最好用的方法了
package ThreadPoolDemo;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.ThreadFactory;/** * user:ypc; * date:2021-06-13; * time: 11:12; */public class ThreadFactoryDemo { public static void main(String[] args) {MyThreadFactory myThreadFactory = new MyThreadFactory();ExecutorService executorService = Executors.newFixedThreadPool(10,myThreadFactory);for (int i = 0; i < 10; i++) { executorService.execute(new Runnable() {@Overridepublic void run() { System.out.println('使用線程工廠設(shè)置的線程名:'+ Thread.currentThread().getName() + ' 使用線程工廠設(shè)置的線程的優(yōu)先級' + Thread.currentThread().getPriority());} });} } private static int count = 0; static class MyThreadFactory implements ThreadFactory{ @Override public Thread newThread(Runnable r) { Thread thread = new Thread(r); thread.setPriority(8); thread.setName('thread--' + count++); return thread; } }}
本篇文章就到這里了,希望可以對你有所幫助,也希望您能夠多多關(guān)注好吧啦網(wǎng)的更多內(nèi)容!
相關(guān)文章:
1. ASP刪除img標(biāo)簽的style屬性只保留src的正則函數(shù)2. asp(vbscript)中自定義函數(shù)的默認(rèn)參數(shù)實現(xiàn)代碼3. 如何使用瀏覽器擴展篡改網(wǎng)頁中的JS 文件4. JSP servlet實現(xiàn)文件上傳下載和刪除5. jsp中sitemesh修改tagRule技術(shù)分享6. Ajax實現(xiàn)表格中信息不刷新頁面進行更新數(shù)據(jù)7. 爬取今日頭條Ajax請求8. JavaWeb Servlet中url-pattern的使用9. ASP基礎(chǔ)知識VBScript基本元素講解10. jsp EL表達式詳解
