Java_多線程

? ? ? ? 有了多線程,我們就可以讓程序同時做多件事情

作用:

? ? ? ? 提高效率

應用場景:

? ? ? ? 只要想讓多個事情同時運行就需要用到多線程

? ? ? ? 比如:軟件中的耗時操作、所有的聊天軟件、所有的服務器...

并發和并行

? ? ? ?并發:在同一時刻,有多個指令在單個CPU上交替執行

? ? ? ? 并行:在同一時刻,有多個指令在多個CPU上同時執行

多線程的實現方式:

①繼承Thread類的方式進行實現

實現步驟:

????????1.自己定義一個類繼承Thread

????????2.重寫run方法

????????3.創建子類對象,并啟動線程

代碼演示:
? ? ? ? MyThread類(繼承Thread):
public class MyThread extends Thread {@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(getName() + "aaa");}}
}
? ? ? ? 測試類ThreadDemo1:
public class ThreadDemo1 {public static void main(String[] args) {/*1.創建類繼承Thread2.重寫run方法3.創建子類對象,啟動線程*///創建子類對象MyThread t1 = new MyThread();MyThread t2 = new MyThread();//給線程起名字便于觀察結果t1.setName("線程1");t2.setName("線程2");//啟動線程t1.start();t2.start();}
}
? ? ? ? 運行結果:

(只截取了一部分)兩個進程并發執行

????????

②實現Runnable接口的方式進行實現

?實現步驟:

????????1.定義一個類implements Runnable接口

????????2.重寫run方法

????????3.創建這個類的對象

????????4.創建線程對象,啟動線程

代碼演示:
? ? ? ? MyRun類(實現Runnable):
public class MyRun implements Runnable{@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName() + "aaa");}}
}
? ? ? ? 測試類ThreadDemo2:
public class ThreadDemo2 {public static void main(String[] args) {/*1.定義一個類implements Runnable接口2.重寫run方法3.創建這個類的對象4.創建線程對象,啟動線程*///創建這個類的對象MyRun mr = new MyRun();//創建線程對象Thread t1 = new Thread(mr);Thread t2 = new Thread(mr);//起名字t1.setName("線程1");t2.setName("線程2");//啟動線程t1.start();t2.start();}
}
? ? ? ? 運行結果:

(只截取了一部分)兩個進程并發執行

????????

★③利用Callable接口和Future接口方式實現

代碼演示:

?? ? ? ? MyCallable類(實現Callable):
public class MyCallable implements Callable<Integer> {@Overridepublic Integer call() throws Exception {int sum = 0;for (int i = 0; i < 100; i++) {sum = sum + i;}return sum;}
}
? ? ? ? 測試類ThreadDemo3:
public class ThreadDemo3 {public static void main(String[] args) throws ExecutionException, InterruptedException {/*1.定義一個類MyCallable實現Callable接口2.重寫call方法(有返回值,表示多線程運行的結果)3.創建MyCallable的對象(表示多線程要執行的任務)4.創建FutureTask的對象(作用是管理多線程運行的結果)5.創建Thread類的對象,啟動線程*///創建MyCallable的對象MyCallable mc = new MyCallable();//創建FutureTask的對象FutureTask<Integer> ft = new FutureTask<>(mc);//創建Thread類的對象Thread t1 = new Thread(ft);//啟動線程t1.start();//獲取結果Integer result = ft.get();System.out.println(result);}
}
? ? ? ? 運行結果:

????????

多線程三種實現方式對比

常見成員方法:

????????其中前四種方法比較簡單,在此簡單介紹幾點

? ? ? ? 1.線程的默認名字是Thread-序號,序號從0開始,隨著進程創建按順序逐個+1

? ? ? ? 2.第三個第四個方法都是哪個線程實現這兩個成員方法所在的方法,則是對這個線程操作

? ? ? ? 3.讓線程休眠后續代碼也會運行

優先級:

? ? ? ? 線程的優先級從1~10分為10擋,1為優先級最低,10為最高。

? ? ? ? 優先級越高,在線程并發中搶到CPU的概率就更高(但不是絕對的)。

? ? ? ? 線程的默認優先級都為5。

代碼演示:
MyThread類:
public class MyThread extends Thread {@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(getName() + ":" + i);}}
}
測試類ThreadDemo4:
public class ThreadDemo4 {public static void main(String[] args) {//創建進程MyThread t1 = new MyThread();MyThread t2 = new MyThread();//設置優先級t1.setPriority(10);t2.setPriority(1);//設置名字t1.setName("線程1");t2.setName("線程2");t1.start();t2.start();}
}
運行結果:

守護線程:

? ? ? ? 特點:

? ? ? ? ? ? ? ? 當其他非守護線程執行完畢之后,守護線程也會陸續結束,不管是否執行完代碼。

代碼演示:
MyThread1:
public class MyThread1 extends Thread {@Overridepublic void run() {for (int i = 0; i <= 100; i++) {System.out.println(getName() + ":" + i);}}
}
MyThread2:
public class MyThread2 extends Thread {@Overridepublic void run() {for (int i = 0; i <= 10; i++) {System.out.println(getName() + ":" + i);}}
}
測試類ThreadDemo5:
public class ThreadDemo5 {public static void main(String[] args) {//創建進程MyThread1 t1 = new MyThread1();MyThread2 t2 = new MyThread2();//設置名字t1.setName("沸羊羊");t2.setName("美羊羊");t1.setDaemon(true);t1.start();t2.start();}
}
運行結果:

出讓線程:

? ? ? ? 執行Thread.yield命令后就是把CPU的執行權交出,然后各個線程重新搶奪。

插隊線程:

? ? ? ? 執行方法 ‘線程對象.join()’ 后 表示把這個線程,插入到當前線程之前,先執行完這個線程,再執行當前方法的線程。

線程的生命周期

線程的生命周期是:新建狀態,就緒狀態,運行狀態,阻塞狀態,死亡狀態

虛擬機中線程的六種狀態

新建狀態、就緒狀態、阻塞狀態、等待狀態、計時等待、結束狀態 (沒有運行狀態)

多線程代碼編寫核心邏輯

? ? ? ? 1.循環

? ? ? ? 2.同步代碼塊

????????3.判斷共享數據是否到了末尾(到了末尾)

? ? ? ? 4.判斷共享數據是否到了末尾(沒到末尾,執行核心邏輯)

同步代碼塊:

格式:

? ? ? ? synchronized (鎖對象) {

? ? ? ? ? ? ? ? 同步代碼塊

????????}

特點:

? ? ? ? 當一個線程搶到CPU的執行權進入到同步代碼塊中執行代碼了,那么其他的線程是不能再進來同步代碼塊的,只有當成功進入的線程執行完畢出去之后,它的鎖才打開,其他的線程才能進來,而且也只能進去一個線程。

? ? ? ? 并且,如果鎖對象不唯一,那么相當于有好幾把鎖,就可能出現不同的線程看的是不同的鎖,還是會同時進去執行代碼。一般我們用當前類的字節碼文件作為鎖對象(類名.class).

? ? ? ? 先結合一個小練習進行代碼演示

練習:

? ? ? ? 某電影院目前正在上映國產大片,共有1000張票,而它有3個賣票窗口,請設計一個程序模擬該電影院買票。

代碼演示:
MyThread3類:
public class MyThread3 extends Thread {//表示這個類所有的對象,都共享ticket數據static int ticket = 0;//鎖對象,必須是唯一的@Overridepublic void run() {while (true) {//同步代碼塊synchronized (MyThread3.class) {if(ticket < 1000) {try {Thread.sleep(10);} catch (InterruptedException e) {throw new RuntimeException(e);}ticket++;System.out.println(Thread.currentThread().getName() + "正在賣第" + ticket + "張票");} else {break;}}}}
}
測試類ThreadDemo6:
public class ThreadDemo6 {public static void main(String[] args) {//某電影院目前正在上映國產大片,共有1000張票,而它有3個賣票窗口,請設計一個程序模擬該電影院買票。//創建三個線程對象MyThread3 t1 = new MyThread3();MyThread3 t2 = new MyThread3();MyThread3 t3 = new MyThread3();//設置名字t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");//啟動線程t1.start();t2.start();t3.start();}
}
運行結果:

同步方法:

? ? ? ? 同步方法就是把synchronized關鍵字加到方法上

格式:

? ? ? ? 修飾符 synchronized 返回值類型 方法名(方法參數) {...}

特點:

? ? ? ? 1.同步方法是鎖住方法里面所有的代碼

? ? ? ? 2.鎖對象不能自己指定(非靜態方法中是this,靜態方法中是當前類的字節碼文件對象)

? ? ? ? 繼續結合上述小練習進行代碼演示

練習:

? ? ? ? 某電影院目前正在上映國產大片,共有1000張票,而它有3個賣票窗口,請設計一個程序模擬該電影院買票。

代碼演示:
MyRunnable類:
public class MyRunnable implements Runnable {int ticket = 0;@Overridepublic void run() {while(true) {if (method()) break;}}//同步方法private synchronized boolean method() {if(ticket == 1000) {return true;} else {try {Thread.sleep(10);} catch (InterruptedException e) {throw new RuntimeException(e);}ticket++;System.out.println(Thread.currentThread().getName() + "正在賣第" + ticket + "張票");}return false;}
}
測試類ThreadDemo7:
public class ThreadDemo7 {public static void main(String[] args) {//某電影院目前正在上映國產大片,共有1000張票,而它有3個賣票窗口,請設計一個程序模擬該電影院買票。MyRunnable mr = new MyRunnable();Thread t1 = new Thread(mr);Thread t2 = new Thread(mr);Thread t3 = new Thread(mr);t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");t1.start();t2.start();t3.start();}
}
運行結果:

lock鎖:

????????繼續結合上述小練習進行代碼演示

練習:

? ? ? ? 某電影院目前正在上映國產大片,共有1000張票,而它有3個賣票窗口,請設計一個程序模擬該電影院買票。

代碼演示:
MyThread類:
public class MyThread extends Thread {static int ticket = 0;//創建鎖對象static Lock lock = new ReentrantLock();@Overridepublic void run() {while(true) {lock.lock();try {if(ticket == 1000) {break;} else {Thread.sleep(10);ticket++;System.out.println(Thread.currentThread().getName() + "正在賣第" + ticket + "張票");}} catch (InterruptedException e) {throw new RuntimeException(e);} finally {lock.unlock();}}}
}
測試類ThreadDemo8:
public class ThreadDemo8 {public static void main(String[] args) {//某電影院目前正在上映國產大片,共有1000張票,而它有3個賣票窗口,請設計一個程序模擬該電影院買票。//創建線程MyThread t1 = new MyThread();MyThread t2 = new MyThread();MyThread t3 = new MyThread();//設置名字t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");//啟動線程t1.start();t2.start();t3.start();}
}
運行結果:

死鎖:

概念:

? ? ? ? 死鎖是多線程中的一種錯誤

????????舉個例子:

????????兩個人在一起吃飯,桌上只有一雙筷子,有三個條件

????????????????①每次需要拿起筷子才能吃飯

????????????????②一次只能拿一只筷子

????????????????③拿到一雙筷子后可以吃一口

? ? ? ? 在一個人拿到一只筷子,另一個人也拿到一只筷子時,這時候就發生了死鎖,程序無法結束

注意:

? ? ? ? 關于死鎖需要注意,在設計程序時盡量避免發生鎖的嵌套

生產者和消費者(等待喚醒機制)

1.基本寫法:

? ? ? ? 假如現在有一個廚師和一個食客,食客吃10份食物就飽了,當桌子上有食物時,食客就會吃一份,吃完通知廚師再做一份,如果沒有食物,就等待。廚師在桌子上沒有食物時,就再做一份,如果有食物則等待。

代碼演示:
桌子Desk類:
public class Desk {//定義變量表示桌子上是否有食物 0:沒有 1:有public static int foodFlag = 0;//定義變量存儲食客還能吃幾碗 食客最多能吃10碗 初始值為10public static int count = 10;//鎖對象public static Object lock = new Object();
}
食客Foodie類:
public class Foodie extends Thread {@Overridepublic void run() {while (true) {synchronized (Desk.lock) {//判斷還能吃嗎if(Desk.count == 0) {//不能吃了break;} else {//還能吃//判斷桌子上有沒有食物if(Desk.foodFlag == 0) {//如果沒有食物//等待(調用鎖對象的wait方法讓食客線程阻塞)try {Desk.lock.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}} else {//如果有食物Desk.count--;if(Desk.count == 0) {System.out.println("食客吃完了一份食物,吃飽了");} else {System.out.println("食客吃完了一份食物,還能吃" + Desk.count + "份");}//將桌子上置為沒有食物Desk.foodFlag = 0;//通知廚師Desk.lock.notifyAll();//喚醒這個鎖對象內的所有線程}}}}}
}
廚師Cooker類:
public class Cooker extends Thread {@Overridepublic void run() {while (true) {synchronized (Desk.lock) {//判斷食客是否吃飽了if(Desk.count == 0) {//吃飽了break;} else {//沒吃飽//判斷桌子上是否有食物if(Desk.foodFlag == 1) {//有食物try {Desk.lock.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}} else {//沒有食物System.out.println("廚師制作好了食物放到了桌子上");//將桌子置為有食物Desk.foodFlag = 1;//喚醒食客Desk.lock.notifyAll();//喚醒這個鎖對象內的所有線程}}}}}
}
測試類Test:
public class Test {public static void main(String[] args) {//創建線程對象Foodie foodieThread = new Foodie();Cooker cookerThread = new Cooker();//啟動線程foodieThread.start();cookerThread.start();}
}
運行結果:

2.利用阻塞隊列:

????????假如現在有一個廚師和一個食客,食客吃10份食物就飽了,廚師可以不斷的做好食物并放到窗口上,窗口上最多可以放3碗。當窗口上有食物時,食客就會吃,如果沒有食物,就等待。

代碼演示:
食客Foodie類:
public class Foodie extends Thread {ArrayBlockingQueue<String> queue;public Foodie(ArrayBlockingQueue<String> queue) {this.queue = queue;}@Overridepublic void run() {while (true) {try {String food = queue.take();System.out.println("食客吃了一份" + food);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}
廚師Cooker類:
public class Cooker extends Thread {ArrayBlockingQueue<String> queue;public Cooker(ArrayBlockingQueue<String> queue) {this.queue = queue;}@Overridepublic void run() {while (true) {try {queue.put("面條");System.out.println("廚師放了一碗面條");} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}
測試類Test:
public class Test {public static void main(String[] args) {/*假如現在有一個廚師和一個食客,廚師可以不斷的做好食物并放到窗口上,窗口上最多可以放3碗。當窗口上有食物時,食客就會吃,如果沒有食物,就等待。*/ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(3);Foodie foodieThread = new Foodie(queue);Cooker cookerThread = new Cooker(queue);foodieThread.start();cookerThread.start();}
}
運行結果:

(因為輸出語句在鎖的外面,所以輸出結果不一定和真是數據傳輸一致)

練習題

練習一:

????????一共有1000張電影票,可以在兩個窗口領取,假設每次領取的時間為3000毫秒,

????????要求:用多線程模擬賣票過程并打印剩余電影票的數量

代碼演示:
MyThread類:
public class MyThread extends Thread {//定義還剩多少票static int ticket = 1000;@Overridepublic void run() {while(true) {synchronized (MyThread.class) {//判斷是否還有票if(ticket == 0) {//沒票了break;} else {//有票//每次領取時間3000毫秒try {sleep(3000);} catch (InterruptedException e) {throw new RuntimeException(e);}ticket--;if(ticket != 0) {System.out.println(getName() + "賣了一張票,還剩" + ticket + "張票");} else {System.out.println(getName() + "賣了一張票,票賣完了");}}}}}
}
測試類Test:
public class Test {public static void main(String[] args) {/*一共有1000張電影票,可以在兩個窗口領取,假設每次領取的時間為3000毫秒,要求:用多線程模擬賣票過程并打印剩余電影票的數量*///創建線程對象MyThread t1 = new MyThread();MyThread t2 = new MyThread();//設置名字t1.setName("窗口1");t2.setName("窗口2");//啟動線程t1.start();t2.start();}
}
運行結果:

?練習二:

????????有100份禮品,兩人同時發送,當剩下的禮品小于10份的時候則不再發出

????????利用多線程模擬該過程并將線程的名字和禮物的剩余數量打印出來

代碼演示:
MyThread類:
public class MyThread extends Thread {//剩余禮物數量static int gift = 100;@Overridepublic void run() {while (true) {synchronized (MyThread.class) {//判斷禮物數量if(gift == 9) {//禮物數量小于10break;} else {//禮物數量不小于10gift--;System.out.println(getName() + "分發了一份禮物,還剩" + gift + "份");}}}}
}
測試類Test:
public class Test {public static void main(String[] args) {/*有100份禮品,兩人同時發送,當剩下的禮品小于10份的時候則不再發出利用多線程模擬該過程并將線程的名字和禮物的剩余數量打印出來*///創建線程對象MyThread t1 = new MyThread();MyThread t2 = new MyThread();//設置名字t1.setName("分發員1");t2.setName("分發員2");//啟動線程t1.start();t2.start();}
}
運行結果:

?練習三:

????????同時開啟了兩個線程,共同獲取1~100之間的所有數字

????????要求:輸出所有的奇數

代碼演示:
MyThread類:
public class MyThread extends Thread {static int num = 1;@Overridepublic void run() {while (true) {synchronized (MyThread.class) {if(num > 100) {break;} else {if(num % 2 == 1) {System.out.println(getName() + ":" + num);}num++;}}}}
}
測試類Test:
public class Test {public static void main(String[] args) {/*同時開啟了兩個線程,共同獲取1~100之間的所有數字要求:輸出所有的奇數*/MyThread t1 = new MyThread();MyThread t2 = new MyThread();t1.start();t2.start();}
}
運行結果:

?練習四:

????????搶紅包也用到了多線程

????????假設:100塊,分成了3個包,現在有五個人去搶

????????其中,紅包是共享數據 5個人是5條線程

????????打印結果如下:

????????????????XXX搶到了XXX元

????????????????XXX搶到了XXX元

????????????????XXX搶到了XXX元

????????????????XXX沒搶到

????????????????XXX沒搶到

代碼演示:
MyThread類:
public class MyThread extends Thread {//紅包個數static int count = 3;//紅包中剩余金額static BigDecimal money = BigDecimal.valueOf(100);//紅包金額最小值static final BigDecimal MIN = BigDecimal.valueOf(0.01);@Overridepublic void run() {//一人搶一次所以不用循環synchronized (MyThread.class) {//對紅包數量判斷if(count == 0) {//搶完了System.out.println(getName() + "沒搶到紅包");} else {//沒搶完BigDecimal prize;if(count == 1) {//只剩一個紅包prize = money;} else {//還剩多個紅包Random r = new Random();double bounds = money.subtract(MIN.multiply(BigDecimal.valueOf(count - 1))).doubleValue();prize = BigDecimal.valueOf(r.nextDouble(bounds));if (prize.compareTo(MIN) == -1) {//prize小于MINprize = MIN;}}//設置保留兩位小數,四舍五入prize = prize.setScale(2, RoundingMode.HALF_UP);System.out.println(getName() + "搶到了" + prize + "元");//從紅包金額中減去搶到的金額money = money.subtract(prize);//紅包個數減一count--;}}}
}
測試類Test:
public class Test {public static void main(String[] args) {/*搶紅包也用到了多線程假設:100塊,分成了3個包,現在有五個人去搶其中,紅包是共享數據5個人是5條線程打印結果如下:XXX搶到了XXX元XXX搶到了XXX元XXX搶到了XXX元XXX沒搶到XXX沒搶到*/MyThread t1 = new MyThread();MyThread t2 = new MyThread();MyThread t3 = new MyThread();MyThread t4 = new MyThread();MyThread t5 = new MyThread();t1.setName("小A");t2.setName("小B");t3.setName("小C");t4.setName("小D");t5.setName("小E");t1.start();t2.start();t3.start();t4.start();t5.start();}
}
運行結果:

???????

?練習五:

????????有一個抽獎池,該抽獎池中存放了獎勵的金額,

????????該抽獎池中的獎項為: {10,5,20,50,100,200,500,800,2,80,300,700}

????????創建兩個抽獎箱(線程) 隨機從抽獎池中獲取獎項元素并打印在控制臺上

????????格式如下:

????????????????抽獎箱1產生了一個10元大獎

????????????????抽獎箱2產生了一個100元大獎

????????????????抽獎箱2產生了一個800元大獎

????????????????抽獎箱1產生了一個200元大獎

????????????????...

代碼演示:
MyThread類:
public class MyThread extends Thread {ArrayList<Integer> list;public MyThread(ArrayList<Integer> list) {this.list = list;}@Overridepublic void run() {while((true)) {synchronized (MyThread.class) {if(list.isEmpty()) {//集合為空break;} else {//集合不為空Collections.shuffle(list);int prize = list.remove(0);System.out.println(getName() + "產生了一個" + prize + "元大獎");}}//鎖外睡10毫秒,避免結果只出現一個線程try {sleep(10);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}
測試類Test:
public class Test {public static void main(String[] args) {/*有一個抽獎池,該抽獎池中存放了獎勵的金額,該抽獎池中的獎項為:{10,5,20,50,100,200,500,800,2,80,300,700}創建兩個抽獎箱(線程)隨機從抽獎池中獲取獎項元素并打印在控制臺上格式如下:抽獎箱1產生了一個10元大獎抽獎箱2產生了一個100元大獎抽獎箱2產生了一個800元大獎抽獎箱1產生了一個200元大獎...*///創建抽獎池ArrayList<Integer> list = new ArrayList<>();Collections.addAll(list,10,5,20,50,100,200,500,800,2,80,300,700);MyThread t1 = new MyThread(list);MyThread t2 = new MyThread(list);t1.setName("抽獎箱1");t2.setName("抽獎箱2");t1.start();t2.start();}
}
運行結果:

????????????????????????????

?練習五Pro1:

????????在上一題(練習五)的基礎上繼續完成如下需求:

???????? 每次抽的過程中,不打印,抽完時一次性打印(隨機)

????????格式如下:

????????????????在此次抽獎過程中,抽獎箱1總共產生了6個獎項

????????????????分別為:10,20,100,500,2,300最高獎項為300元,總計額為932元

????????????????在此次抽獎過程中,抽獎箱2總共產生了6個獎項

????????????????分別為:5,50,200,800,80,700最高獎項為800元,總計額為1835元

代碼演示:
MyThread類:
public class MyThread extends Thread {ArrayList<Integer> list;int max = 0;int sum = 0;public MyThread(ArrayList<Integer> list) {this.list = list;}public String boxToString(ArrayList<Integer> box) {StringBuffer sb = new StringBuffer();for (int i = 0; i < box.size(); i++) {if (i != box.size() - 1) {sb.append(box.get(i) + ",");} else {sb.append(box.get(i));}}return sb.toString();}@Overridepublic void run() {ArrayList<Integer> box = new ArrayList<>();while ((true)) {synchronized (com.han.thread.test5.MyThread.class) {if (list.isEmpty()) {//集合為空System.out.println("在此次抽獎過程中," + getName() + "總共產生了" + box.size() + "個獎項\n" +"分別為:" + boxToString(box) + "最高獎項為" + max + "元,總計額為" + sum + "元");break;} else {//集合不為空Collections.shuffle(list);int prize = list.remove(0);box.add(prize);sum = sum + prize;if (prize > max) {max = prize;}//System.out.println(getName() + "產生了一個" + prize + "元大獎");}}//鎖外睡10毫秒,避免結果只出現一個線程try {sleep(10);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}
測試類Test:
public class Test {public static void main(String[] args) {/*在上一題(練習五)的基礎上繼續完成如下需求:每次抽的過程中,不打印,抽完時一次性打印(隨機)格式如下:在此次抽獎過程中,抽獎箱1總共產生了6個獎項分別為:10,20,100,500,2,300最高獎項為300元,總計額為932元在此次抽獎過程中,抽獎箱2總共產生了6個獎項分別為:5,50,200,800,80,700最高獎項為800元,總計額為1835元*///創建抽獎池ArrayList<Integer> list = new ArrayList<>();Collections.addAll(list,10,5,20,50,100,200,500,800,2,80,300,700);MyThread t1 = new MyThread(list);MyThread t2 = new MyThread(list);t1.setName("抽獎箱1");t2.setName("抽獎箱2");t1.start();t2.start();}
}
運行結果:

???????????????????????????????????

?練習五Pro2:

????????在上一題基礎上繼續完成如下需求:

???????? 每次抽的過程中,不打印,抽完時一次性打印(隨機)

????????格式如下:

????????????????在此次抽獎過程中,抽獎箱1總共產生了6個獎項

????????????????分別為:10,20,100,500,2,300最高獎項為300元,總計額為932元

????????????????在此次抽獎過程中,抽獎箱2總共產生了6個獎項

????????????????分別為:5,50,200,800,80,700最高獎項為800元,總計額為1835元

????????????????在此次抽獎過程中,抽獎箱2中產生了最大獎項,該獎項金額為800元

代碼演示:
MyCallable類:
public class MyCallable implements Callable<Integer> {ArrayList<Integer> list;public MyCallable(ArrayList<Integer> list) {this.list = list;}public String boxToString(ArrayList<Integer> box) {StringBuffer sb = new StringBuffer();for (int i = 0; i < box.size(); i++) {if (i != box.size() - 1) {sb.append(box.get(i) + ",");} else {sb.append(box.get(i));}}return sb.toString();}@Overridepublic Integer call() throws Exception {int max = 0;int sum = 0;ArrayList<Integer> box = new ArrayList<>();while ((true)) {synchronized (com.han.thread.test5.MyThread.class) {if (list.isEmpty()) {//集合為空System.out.println("在此次抽獎過程中," + Thread.currentThread().getName() + "總共產生了" + box.size() + "個獎項\n" +"分別為:" + boxToString(box) + "最高獎項為" + max + "元,總計額為" + sum + "元");break;} else {//集合不為空Collections.shuffle(list);int prize = list.remove(0);box.add(prize);sum = sum + prize;if (prize > max) {max = prize;}//System.out.println(getName() + "產生了一個" + prize + "元大獎");}}//鎖外睡10毫秒,避免結果只出現一個線程Thread.sleep(10);}if(box.isEmpty()) {return null;} else {return Collections.max(box);}}}
測試類Test:
public class Test {public static void main(String[] args) throws ExecutionException, InterruptedException {/*在上一題基礎上繼續完成如下需求:每次抽的過程中,不打印,抽完時一次性打印(隨機)格式如下:在此次抽獎過程中,抽獎箱1總共產生了6個獎項分別為:10,20,100,500,2,300最高獎項為300元,總計額為932元在此次抽獎過程中,抽獎箱2總共產生了6個獎項分別為:5,50,200,800,80,700最高獎項為800元,總計額為1835元在此次抽獎過程中,抽獎箱2中產生了最大獎項,該獎項金額為800元*///創建抽獎池ArrayList<Integer> list = new ArrayList<>();Collections.addAll(list, 10, 5, 20, 50, 100, 200, 500, 800, 2, 80, 300, 700);//創建多線程要運行的參數對象MyCallable mc = new MyCallable(list);//創建多線程運行結果的管理者對象FutureTask<Integer> ft1 = new FutureTask<>(mc);FutureTask<Integer> ft2 = new FutureTask<>(mc);//創建線程對象Thread t1 = new Thread(ft1);Thread t2 = new Thread(ft2);//設置名字t1.setName("抽獎箱1");t2.setName("抽獎箱2");//啟動t1.start();t2.start();//輸出結果int max1 = ft1.get();int max2 = ft2.get();if (max1 > max2) {System.out.println("在此次抽獎過程中,抽獎箱1中產生了最大獎項,該獎項金額為" + max1 + "元");} else {System.out.println("在此次抽獎過程中,抽獎箱2中產生了最大獎項,該獎項金額為" + max2 + "元");}}
}
運行結果:

???????????????????????????????????

線程池

核心原理:

? ? ? ? ①創建一個池子,池子中是空的

? ? ? ? ②提交任務時,池子會創建新的線程對象,任務執行完畢,線程歸還給池子,下回再次提交任務時,不需要創建新的線程,直接復用已有的線程即可

? ? ? ? ③但是如果提交任務時,池子中沒有空閑的線程,也無法創建新的線程,任務就會排隊等待

創建方法:

代碼演示:

MyRunnable類:
public class MyRunnable implements Runnable {@Overridepublic void run() {for (int i = 1; i <= 1000; i++) {System.out.println(Thread.currentThread().getName() + "-" + i);}}
}
測試類ThreadPoolDemo1:
public class ThreadPoolDemo1 {public static void main(String[] args) throws InterruptedException {//創建一個沒有上限的線程池(上限二十一億多,但是創建不了這么多機器就承受不住)ExecutorService pool1 = Executors.newCachedThreadPool();System.out.println("無上限線程池");pool1.submit(new MyRunnable());pool1.submit(new MyRunnable());pool1.submit(new MyRunnable());pool1.submit(new MyRunnable());pool1.submit(new MyRunnable());//銷毀線程池pool1.shutdown();//創建一個有上線的線程池ExecutorService pool2 = Executors.newFixedThreadPool(3);System.out.println("有上限線程池");pool2.submit(new MyRunnable());pool2.submit(new MyRunnable());pool2.submit(new MyRunnable());pool2.submit(new MyRunnable());pool2.submit(new MyRunnable());//銷毀線程池pool2.shutdown();}
}

自定義線程池

核心原理:

????????①創建一個池子,池子中是空的

? ? ? ? ②有任務提交時,線程池會創建線程去執行任務,執行完畢歸還線程

不斷的提交任務,會有以下三個臨界點

? ? ? ? ①當核心線程滿時,再提交任務就會排隊

? ? ? ? ②當核心線程滿,隊伍滿時,會創建臨時線程

? ? ? ? ③當核心線程滿,隊伍滿,臨時線程滿時,會觸發任務拒絕策略

任務拒絕策略:

創建代碼演示:

public class ThreadPoolDemo2 {public static void main(String[] args) {/*ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(核心線程數量,最大線程數量,空閑線程最大存活時間,時間單位,任務隊列,創建線程工廠,任務的拒絕策略)參數一:核心線程數量              不能小于0參數二:最大線程數量              不能小于0,最大數量>=核心線程數量參數三:空閑線程最大存活時間       不能小于0參數四:時間單位                  用TimeUnit指定參數五:任務隊列                  不能為null參數六:創建線程工廠              不能為null參數七:任務的拒絕策略             不能為null*/ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3,6,60,TimeUnit.SECONDS,new ArrayBlockingQueue<>(3),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());}
}

線程池多大合適

前置概念:

最大并行數:

? ? ? ? 如果電腦是四核八線程,那么最大并行數就是8

? ? ? ? 我的電腦是八核十六線程,所以最大并行數是16

1.CPU密集型運算:

? ? ? ? 計算比較多,讀取本地文件或者數據庫的操作比較少

線程池大小:

? ? ? ? 這種情況線程池大小最好為最大并行數+1=17,多出來那一個用來候補,,在遇到問題時頂上去

2.I/O密集型運算:

????????讀取本地文件或者數據庫的操作比較多

線程池大小:

? ? ? ? 有下面公式求出

???????

????????這里期望CPU利用率我們設為100%,假如要從本地文件中讀取兩個數據并進行相加,獲取數據由硬盤完成,不計算在CPU計算時間中,假設讀取數據用1秒,CPU計算用1秒,那么總時間就為2秒,線程池大小就等于16(最大并行數)* 100% * 2/1 = 32

? ? ? ? CPU計算時間可以通過thread dump工具類獲取,然后計算

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/14853.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/14853.shtml
英文地址,請注明出處:http://en.pswp.cn/web/14853.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

AI是否可統計人類理性和感性的非線性?

一、背景 從控制理論的角度來看&#xff0c;“人類理性和感性的非線性”可以類比為動態系統中非線性元件的行為特性。在控制理論中&#xff0c;非線性意味著系統的輸出不再嚴格與其輸入成比例&#xff0c;也就是說&#xff0c;同樣的輸入條件下可能會導致不同的結果&#xff0…

為什么defineComponent組件名字和文件名字不能一樣

今天在開發新組件時&#xff0c;在使用defineComponent定義組件時&#xff0c;把name寫成了文件名的首字母大寫法導致項目一直報錯。找了很久才知道是名字的問題。 defineComponent組件名字和文件名字不能一樣 在某些情況下&#xff0c;將組件的名稱與文件名相同可能會導致一些…

當面試官問出“Unsafe”類時,我就知道這場面試廢了,祖墳都能給你問出來!

一、寫在開頭 依稀記得多年以前的一場面試中&#xff0c;面試官從Java并發編程問到了鎖&#xff0c;從鎖問到了原子性&#xff0c;從原子性問到了Atomic類庫&#xff08;對著JUC包進行了刨根問底&#xff09;&#xff0c;從Atomic問到了CAS算法&#xff0c;緊接著又有追問到了…

【c語言】函數遞歸

在C語言中&#xff0c;函數遞歸是一種函數調用自身的技術。遞歸函數通常用于解決可以分解為更小、類似子問題的問題。遞歸函數有兩個基本部分&#xff1a; 基本情況&#xff08;Base Case&#xff09;&#xff1a;這是遞歸的終止條件&#xff0c;即函數停止遞歸并返回值的條件…

Mysql with 用法

什么是 with 語句 WITH 子句是 MySQL 中的一種 SQL 結構&#xff0c;又稱為 Common Table Expression (CTE)。它在不影響原有 SQL 語句的情況下&#xff0c;允許開發人員臨時創建一個內存中的結果集&#xff0c;然后對其進行操作。 with 語句用法 語法格式 WITH cte_name (…

JVM-調優之-高cpu線程問題排查

這里主要是對jstack命令的使用&#xff1b; 程序在運行過程中卡主&#xff0c;前端無法訪問&#xff0c;一看服務器CPU占用達到200到300%多。 排查思路 1&#xff09;找出占用高的進程 2&#xff09;找出占用高的線程 3&#xff09;找出具體的代碼 分析步驟&#xff1a; …

C++里的vector詳細講解

在C的標準模板庫&#xff08;STL&#xff09;中&#xff0c;vector是一個非常有用的動態數組容器。它允許我們存儲可變大小的同類型元素序列&#xff0c;并且能夠動態地增長和縮小。由于其靈活性和易用性&#xff0c;vector在C編程中得到了廣泛的應用。 一、vector的基本操作 …

Java異常處理:最佳實踐與常見模式

在Java編程中&#xff0c;異常處理是保證程序健壁性和穩定性的重要方面。良好的異常處理不僅可以幫助程序在面對錯誤情況時恢復到正常狀態&#xff0c;還可以提供錯誤診斷的信息&#xff0c;輔助開發者快速定位問題。本文將探討Java中的異常處理機制&#xff0c;包括異常的分類…

esp32 Micropython 長按按鍵動作一次代碼

1. 長按按鍵&#xff0c;松手后動作 from machine import Pin import timeEnter_key Pin(15, Pin.IN, Pin.PULL_UP) Enter_key_flag 0 Enter_key_flag_temp 0while True:if Enter_key.value() 0:time.sleep_ms(10)while Enter_key.value() 0:Enter_key_flag_temp not En…

System.Collections.Generic 中的接口和類型區分

System.Collections.Generic 命名空間包含了許多與泛型集合相關的接口和類。這些接口定義了一組通用的集合行為&#xff0c;而具體的實現&#xff08;如 List、Dictionary<TKey, TValue> 等&#xff09;則遵循這些接口&#xff0c;從而提供具體的集合功能。以下是 System…

前后端開發入門全攻略:零基礎學起

新書上架~&#x1f447;全國包郵奧~ python實用小工具開發教程http://pythontoolsteach.com/3 歡迎關注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目錄 一、前后端開發概覽 二、后端開發基礎&#xff1a;Flask框架入門 代碼案例&#xff1a;Hel…

vue3之使用圖片實現類似于 el-radio 的單選框功能,并且可實現選中和取消選中

背景 我們在工作中常用的一般都是使用類似于 element-plus 中的 el-radio 或者是 el-checkbox 來實現單選或者多選 若有一天我們遇到了一個新的業務需求,需要使用 圖片 來實現類似于 el-radio 的功能,并且要求實現第一次點擊時處于選中狀態,當我們再次點擊時處于非選中狀態…

談戀愛沒經驗?那就來刷談戀愛經驗寶寶吧

??作者主頁&#xff1a;小虛竹 ??作者簡介&#xff1a;大家好,我是小虛竹。2022年度博客之星評選TOP 10&#x1f3c6;&#xff0c;Java領域優質創作者&#x1f3c6;&#xff0c;CSDN博客專家&#x1f3c6;&#xff0c;華為云享專家&#x1f3c6;&#xff0c;掘金年度人氣作…

自動駕駛---Tesla的自動駕駛技術進化史(PerceptionPlanning)

1 前言 筆者在專欄《自動駕駛Planning模塊》中已經詳細講解了傳統自動駕駛Planning模塊的內容&#xff1a;包括行車的Behavior Planning和Motion Planning&#xff0c;以及低速記憶泊車的Planning&#xff08;最開始有15篇&#xff0c;目前逐漸更新到17篇&#xff09;。讀者對整…

【Spring】SSM介紹_SSM整合

1、SSM介紹 1.1簡介 SSM&#xff08;Spring SpringMVC MyBatis&#xff09;整合是一種流行的Java Web應用程序框架組合&#xff0c;它將Spring框架的核心特性、SpringMVC作為Web層框架和MyBatis作為數據訪問層框架結合在一起。這種整合方式提供了從數據訪問到業務邏輯處理再…

5.18 TCP機械臂模擬

#include <netinet/tcp.h>//包含TCP選項的頭文件 #include <arpa/inet.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <linux/input.h>//讀取輸入事件 #include <sys/types.h> #include <sys/stat.h&…

對于mybatis和mybatisplus的選擇

對于mybatis和mybatisplus的選擇 1. 問題2. MP單表操作2.1 單表普通查詢2.2 單表分頁查詢 3. mybatis多表操作3.1 多表普通查詢3.2 多表分頁查詢 1. 問題 mybatis 和 mybatisplus作為當下主流的持久層框架&#xff0c;各有優劣勢。依據個人經驗&#xff1a;mybatis可以定制化輸…

一文詳解邏輯越權漏洞

1. 邏輯越權 1.1. 漏洞原理 邏輯越權漏洞就是當用戶跳過自己的權限限制&#xff0c;去操作同等級用戶或者上級用戶。正常的情況下&#xff0c;當一個用戶去訪問某個資源的時候&#xff0c;首先需要去登錄驗證自己的權限&#xff0c;其次是對數據的查詢&#xff0c;最后返回數…

gateway基本配置,如何配置?

文章推薦 1 作為程序員&#xff0c;開發用過最好用的AI工具有哪些&#xff1f; 2 Github Copilot正版的激活成功&#xff0c;終于可以chat了 3 idea,pycharm等的ai assistant已成功激活 4 新手如何拿捏 Github Copilot AI助手&#xff0c;幫助你提高寫代碼效率 5 Jetbrains的a…

linux命令中arpd的使用

arpd 收集免費ARP信息 補充說明 arpd命令 是用來收集免費arp信息的一個守護進程&#xff0c;它將收集到的信息保存在磁盤上或者在需要時&#xff0c;提供給內核用戶用于避免多余廣播。 語法 arpd(選項)(參數)選項 -l&#xff1a;將arp數據庫輸出到標準輸出設備顯示并退出…