- 資訊首頁(yè) > 開(kāi)發(fā)技術(shù) > 編程語(yǔ)言 >
- Java線(xiàn)程創(chuàng )建(賣(mài)票),線(xiàn)程同步(賣(mài)包子)的實(shí)現示例
如下FileOutputStream源碼中拋出異常,為了讓寫(xiě)代碼人自己寫(xiě)try catch異常提示信息。
package com.itheim07.thread; /* * 進(jìn)程和線(xiàn)程 * 1. 進(jìn)程 : 航空母艦(資源: 燃油 彈藥) * 2. 線(xiàn)程 : 艦載機 * 一個(gè)軟件運行: 一個(gè)軍事活動(dòng), 必須有一艘航母出去,但執行具體任務(wù)的是航母上的艦載機 * 一個(gè)軟件運行,至少一個(gè)進(jìn)程, 一個(gè)進(jìn)程中至少一個(gè)線(xiàn)程。谷歌瀏覽器是多進(jìn)程,進(jìn)程多了,占用資源多,速度快 * * cpu: 4核 8線(xiàn)程。線(xiàn)程要運行,需要cpu授予執行權(指揮室),指揮室可以同時(shí)調度8架 飛機 * 1. 并行 : 同一時(shí)間,同時(shí)執行 (并行只能8線(xiàn)程) * 2. 并發(fā) : 同一段時(shí)間, 實(shí)際上是交替執行, 速度快的時(shí)候看起來(lái)像是同時(shí)執行(頻率快)(常見(jiàn): 并發(fā)1800線(xiàn)程) * * cpu調度算法(并發(fā)) * 1. 分時(shí)調度 : 1800s, 每個(gè)線(xiàn)程1s * 2. 搶占式調度 : 按照線(xiàn)程優(yōu)先級進(jìn)行分配, 優(yōu)先級高(可以自己設置)一般就分配的多(隨機性強) java * * 為什么需要多線(xiàn)程? * 1. 默認java代碼有兩個(gè)線(xiàn)程 * 1. main方法線(xiàn)程 : 主線(xiàn)程 * 2. GC線(xiàn)程(jvm使用的,我們無(wú)法調度) * 2. 一個(gè)線(xiàn)程可用, 有什么局限性?只能做一件事 * 3. 如果想要同時(shí)執行多個(gè)任務(wù) -> 多線(xiàn)程 */ public class ThreadDemo { public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { while(true){ System.out.println("播放音樂(lè )..."); } } }).start(); //.start()不能改成.run() boolean result = true; while(result){ System.out.println("下載電影..."); } /* while(result){ //雖然騙了編譯器,但還是不能執行到這里 System.out.println("播放音樂(lè )..."); }*/ } }
如下線(xiàn)程第一種創(chuàng )建方式。
package com.itheima01.thread; /* Thread:1. start() : 啟動(dòng)線(xiàn)程,jvm會(huì )創(chuàng )建線(xiàn)程,并調用run方法 2. static Thread currentThread(),返回對當前正在執行的線(xiàn)程對象的引用。 3. String getName() : 獲取線(xiàn)程名稱(chēng) !!! Thread.currentThread().getName() : 獲取當前線(xiàn)程名稱(chēng) 線(xiàn)程默認命名規則:1. main線(xiàn)程 : main 2. 子線(xiàn)程(main線(xiàn)程創(chuàng )建的線(xiàn)程) : static int number;static被共享 Thread-0 , 1, 2 ... */ public class ThreadDemo02 { public static void main(String[] args) { // Thread thread = Thread.currentThread(); // String name = thread.getName(); // System.out.println(name); // main //下面一行等同于上面 System.out.println("主:" + Thread.currentThread().getName()); YourThread yt = new YourThread(); yt.start(); //子:Thread-0 YourThread yt2 = new YourThread(); yt.run(); //子:main。 因為子線(xiàn)程YourThread還未執行起飛 ,被main飛機拖著(zhù)走 YourThread yt3 = new YourThread(); yt3.start(); //子:Thread-2。 不是Thread-1是因為yt2未起飛但依舊new了yt2 // Person p = new Person(); //執行空參構造 // System.out.println(p.number); //0 // Person p2 = new Person(); // System.out.println(p2.number); //1 } } class YourThread extends Thread{ @Override public void run() { System.out.println("子:" + Thread.currentThread().getName()); } } class Person{ static int number=-1; public Person(){ number++; } }
package com.itheima02.runnable; /* * 線(xiàn)程第二種創(chuàng )建方式: 1. 聲明實(shí)現 Runnable 接口的類(lèi)。 * 2. 該類(lèi)然后實(shí)現 run 方法。 * 3. 然后可以分配該類(lèi)的實(shí)例,在創(chuàng )建 Thread 時(shí)作為一個(gè)參數來(lái)傳遞并啟動(dòng)。 * Thread(Runnable target) */ public class RunnableDemo { public static void main(String[] args) { MyRunnable mr = new MyRunnable(); // 分配該類(lèi)的實(shí)例 Thread t = new Thread(mr); t.start(); //Thread-0 } } class MyRunnable implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()); } }
package com.itheima02.runnable; //用匿名內部類(lèi)簡(jiǎn)化上面代碼 public class RunnableDemo02 { public static void main(String[] args) { /* Runnable mr = new Runnable(){ //用接口名Runnable代替子類(lèi)類(lèi)名,匿名對象。 //不用再寫(xiě)class MyRunnable implements Runnable{},Runnable mr = new MyRunable(); 向上轉型 @Override public void run() { //new一個(gè)接口()再{},是new這個(gè)接口的子類(lèi)對象 System.out.println(Thread.currentThread().getName()); } }; Thread t = new Thread(mr); t.start(); // new Thread(mr).start(); */ //111111111111111111111111111111111111111111111111111111111111111111111111111111 new Thread(new Runnable() { @Override public void run() { //主要關(guān)注run System.out.println(Thread.currentThread().getName()); } }).start(); //new Thread(() -> System.out.println(Thread.currentThread().getName())).start(); } }
package com.itheima03.ticket; /* * 需求假設某航空公司有三個(gè)窗口發(fā)售某日某次航班的100張票,100張票可以作為共享資源,三個(gè)售票窗口需要創(chuàng )建三個(gè)線(xiàn)程 * 好處: 多線(xiàn)程執行同一任務(wù),比較快 * 1. 程序(單線(xiàn)程) , 并發(fā)1600線(xiàn)程, cpu分配執行權: 1/1600 * 2. 程序(多線(xiàn)程 100) , 并發(fā)1700, cpu分配給我們的程序執行權更多:1/17 * 注意: 線(xiàn)程不是越多越好(線(xiàn)程本身很占內存, 慢。票數不多不需要用多線(xiàn)程) */ public class TicketDemo01 { public static void main(String[] args) { MyWindow mw1 = new MyWindow(); //堆中開(kāi)一塊空間,不加static,number=100進(jìn)堆 mw1.setName("窗口壹"); MyWindow mw2 = new MyWindow(); //同上 mw2.setName("窗口222"); MyWindow mw3 = new MyWindow(); //同上 mw3.setName("窗口三三三"); mw1.start(); mw2.start(); mw3.start(); } } //11111111111111111111111111111111111111111111111111111111111111111111111111111 class MyWindow extends Thread{ static int number = 100; //去掉static,每創(chuàng )建一個(gè)MyWindow窗口在堆里開(kāi)辟一塊空間,三個(gè)窗口各賣(mài)100張 @Override public void run() { while(number > 0){ System.out.println(Thread.currentThread().getName() + "正在賣(mài)出第" + number + "張票"); number--; } } }
/* * 兩種線(xiàn)程創(chuàng )建方式: 1. 繼承Thread * 2. 實(shí)現Runnbale * 如上第二種方案會(huì )更好一些,不需要加static,因為只new了一個(gè)對象 * 1. 實(shí)現接口,而不是繼承類(lèi)(擴展性更強) 接口可以多實(shí)現,但是類(lèi)只能單繼承(MyWindow繼承Thread后,就不能繼承另外的類(lèi)。MyTask可以繼承其他類(lèi),實(shí)現其他接口) * 2. 更符合 面向對象 (高內聚,低耦合:線(xiàn)程獨立,和業(yè)務(wù)代碼MyTask分離,傳入賣(mài)豬肉任務(wù)也行)。封裝(各干各的,有必要再進(jìn)行合作) */
如下線(xiàn)程同步問(wèn)題分析:兩種創(chuàng )建方式3個(gè)窗口都總賣(mài)出102張票,而不是100張。原因:三個(gè)窗口同時(shí)卡在打印正在賣(mài)出第100張票。解決:t1在賣(mài)第100張票時(shí),cpu可能會(huì )切到t3和t2,可以控制t2和t3不動(dòng),等t1的number- -完再動(dòng)。
package com.itheima04.synchronizedd; import java.io.IOException; /* * 1. 代碼塊 * synchronized(鎖對象){ * 代碼A * } * 1. 鎖對象可以是任意對象,但必須唯一 * 2. 同步代碼塊中的 代碼A 同一時(shí)間,只允許一個(gè)線(xiàn)程執行 * 使用同步鎖的注意點(diǎn):1. 在保證業(yè)務(wù)邏輯可用的情況,同步鎖加的范圍越小越好 * * 2. 鎖對象必須唯一:<1> 如果能保證當前對象唯一,this也可以作為鎖對象 (更節省內存) * <2> 當前類(lèi)名.class(最好的鎖對象) -> Class對象(一個(gè)類(lèi)被加載,在內存都會(huì )有一個(gè)Class對象) 反射 */ public class TicketDemo02 { public static void main(String[] args) { MyTask mt = new MyTask(); //上面只new了一個(gè),可以用this Thread t1 = new Thread(mt); t1.setName("窗口壹"); Thread t2 = new Thread(mt); t2.setName("窗口222"); Thread t3 = new Thread(mt); t3.setName("窗口三三三"); t1.start(); t2.start(); t3.start(); } } class MyTask implements Runnable{ int number = 100; // Object obj = new Object(); //鎖對象 @Override public void run() { while(number > 0){ //1111111111111111111111111111111111111111111111111111111111111111111111111111111111111 synchronized(MyTask.class){ //MyTask.class也可以換成this if(number <= 0){ break; //跳出while大循環(huán) } System.out.println(Thread.currentThread().getName() + "正在賣(mài)出第" + number + "張票"); number--; } //111111111111111111111111111111111111111111111111111111111111111111111111111111111111 //這邊只能try catch不能throws,原因:父類(lèi)Runnable中run方法沒(méi)有聲明拋出編譯異常,所以子類(lèi)也不能throws try { Thread.sleep(1); //線(xiàn)程啥事也不干,暫停1ms,cpu有空閑切換其他線(xiàn)程 } catch (InterruptedException e) { e.printStackTrace(); } } //while里 } }
如下t2賣(mài)到0張時(shí)出while,而t1和t3還在while里,此時(shí)number=0,所以變?yōu)?和-1。
如下把synchronized拖到外面也不行。
如下加if(number <= 0),沒(méi)有加浪費時(shí)間代碼,所以看不到交替效果,但不會(huì )出現0和-1。
obj是鎖對象即鑰匙,如下鑰匙不能進(jìn)run方法(每個(gè)線(xiàn)程一把即三把鑰匙了),只能在成員位置。
用this,不用new object(),可以節約內存。
package com.itheima05.method; /* * synchronized 方法(同步方法) * 1. 語(yǔ)法 : 方法聲明 + synchronized * 2. 同步方法有沒(méi)有鎖對象? 有 * 1. 普通方法: 是this * 2. 靜態(tài)方法: 靜態(tài)不能和對象(this)有關(guān)。 是當前類(lèi)名.class */ public class TicketDemo02 { public static void main(String[] args) { MyTask mt = new MyTask(); Thread t1 = new Thread(mt); t1.setName("窗口壹"); Thread t2 = new Thread(mt); t2.setName("窗口222"); Thread t3 = new Thread(mt); t3.setName("窗口三三三"); t1.start(); t2.start(); t3.start(); } } class MyTask implements Runnable{ static int number = 100; @Override public void run() { while(number > 0){ method(); //非靜態(tài)方法可以調用靜態(tài)方法 try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } } private static synchronized void method() { //靜態(tài)方法不能和對象關(guān)鍵字如this相關(guān) //同步方法效果 等價(jià)于 同步代碼塊 if(number <= 0){ return; //break只能寫(xiě)在循環(huán)和switch里 } System.out.println(Thread.currentThread().getName() + "正在賣(mài)出第" + number + "張票"); number--; } }
package com.itheima06.lock; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /* * Lock接口: 1. 實(shí)現類(lèi) ReentrantLock * 2. lock() 獲取鎖(獲取鑰匙) * 3. unlock() 釋放鎖 (還鑰匙) */ public class TicketDemo02 { public static void main(String[] args) { MyTask mt = new MyTask(); Thread t1 = new Thread(mt); t1.setName("窗口壹"); Thread t2 = new Thread(mt); t2.setName("窗口222"); Thread t3 = new Thread(mt); t3.setName("窗口三三三"); t1.start(); t2.start(); t3.start(); } } class MyTask implements Runnable{ int number = 100; Lock lock = new ReentrantLock(); //創(chuàng )建lock對象 @Override public void run() { while(number > 0){ //1111111111111111111111111111111111111111111111111111111111111111111111111 lock.lock(); if(number <= 0){ // System.out.println(Thread.currentThread().getName()); lock.unlock(); // 注意: lock提供了鎖的可視化操作(線(xiàn)程執行結束,要記得手動(dòng)釋放。廁所上完不能帶走鑰匙)//同步代碼塊return或break后是jvm自動(dòng)釋放鎖。//這里不加lock.unlock()程序停不下來(lái)。 break; } System.out.println(Thread.currentThread().getName() + "正在賣(mài)出第" + number + "張票"); number--; lock.unlock(); } } }
如下ThreadLocal相當于一個(gè)map,key就是當前的線(xiàn)程,value就是需要存儲的對象。
t1(…,User),如下情況可將User放入ThreadLocal中,每次通過(guò).get拿到線(xiàn)程的User。
package com.itheima07.bz; public class Demo { public static void main(String[] args) throws InterruptedException { Object obj = new Object(); // obj.wait(); //IllegalMonitorStateException : 非法的監視狀態(tài)異常,因為.wait()必須鎖對象調用如下 synchronized (obj){ //對象變成鎖對象 obj.wait(); //不會(huì )報錯,一直等待。在鎖對象中 } } }
如下兩個(gè)方法wait和notify不是給線(xiàn)程調用的,而是給鎖對象【鎖對象可以是任意對象】調用的如上所示。BaoZi只能一個(gè)線(xiàn)程對其操作。
package com.itheima07.bz; public class BaoZi { boolean isHave=false; //默認沒(méi)有包子 }
package com.itheima07.bz; public class BaoziPu extends Thread { BaoZi bz; public BaoziPu(BaoZi bz){ this.bz = bz; } @Override public void run() { while(true){ //不停生產(chǎn)包子 //111111111111111111111111111111111111111111111111111111111111111111111111111111 synchronized (bz){ //加鎖: 同步代碼,生產(chǎn)包子時(shí)不讓別人打擾我。注意下面wait和notify if(bz.isHave){ try { bz.wait(); //包子鋪有包子就等待(此時(shí)吃貨正在吃包子) } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("包子鋪生產(chǎn)包子..."); //沒(méi)包子 bz.isHave = true; bz.notify(); //喚醒吃貨 } } //while里 } }
package com.itheima07.bz; public class ChiHuo extends Thread{ BaoZi bz; public ChiHuo(BaoZi bz){ this.bz = bz; } @Override public void run() { while(true){ //不停吃包子 //1111111111111111111111111111111111111111111111111111111111111111111111111111 synchronized (bz){ if(!bz.isHave){ try { bz.wait(); //吃貨沒(méi)有包子就等待(此時(shí)包子鋪正在生產(chǎn)包子) } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("吃貨吃包子"); //有包子 bz.isHave = false; bz.notify(); //喚醒包子鋪 } } } }
package com.itheima07.bz; public class BzDemo { public static void main(String[] args) { BaoZi bz = new BaoZi(); BaoziPu bzp = new BaoziPu(bz); //和下面一行共同操作一個(gè)包子對象 ChiHuo ch = new ChiHuo(bz); bzp.start(); ch.start(); } }
如下第一次沒(méi)有包子,所以繞過(guò)2中if到1。運行完1后就有包子了,1時(shí)間很短,cpu不切換線(xiàn)程,切換了也沒(méi)用,因為2中syn…(bz)包子被鎖住,就算切換到吃貨線(xiàn)程進(jìn)不去syn…(bz)里,所以1中notify喚不醒吃貨線(xiàn)程。
1和2都在sy…(bz)里,bzp線(xiàn)程bz.wait()【有3個(gè)好處】進(jìn)入等待狀態(tài)即進(jìn)入監視隊列即等待包子被吃
,吃貨線(xiàn)程的synchronized鎖被打開(kāi),有包子不會(huì )wait,執行3。
一個(gè)線(xiàn)程wait把自己停下來(lái)放入堆(監視隊列)
中,來(lái)年開(kāi)春,另一個(gè)線(xiàn)程中3叫我起來(lái)干活。2和3對應,1和4對應。3喚醒了2中wait,但2沒(méi)鑰匙(鎖)動(dòng)不了(鬼壓床),鑰匙在吃貨手上,所以3往后4執行釋放鎖,1234不停循環(huán)執行。
生產(chǎn)消費者模型:用戶(hù)發(fā)請求來(lái)
相當于包子鋪生產(chǎn)包子即生產(chǎn)者
。服務(wù)器
24小時(shí)開(kāi)著(zhù)相當于消費者
一天24小時(shí)等包子吃。不會(huì )讓消費者線(xiàn)程空轉浪費cpu資源,所以沒(méi)包子設置消費者線(xiàn)程為wait狀態(tài)不占用cpu資源
。
package com.atguigu.test14; // 線(xiàn)程通信是用來(lái)解決生產(chǎn)者與消費者問(wèn)題。 public class Test14 { public static void main(String[] args) { Workbench tai = new Workbench(); //相當于包子 Cook c = new Cook("崔志恒", tai); //生產(chǎn)者 Waiter w = new Waiter("翠花", tai); //消費者 c.start(); w.start(); } } //11111111111111111111111111111111111111111111111111111111111111111111111111 class Workbench{ private static final int MAX = 10; //假設工作臺上最多能夠放10盤(pán) private int count; //count是共用的,要考慮線(xiàn)程安全 public synchronized void put(){ //同步方法,非靜態(tài)方法來(lái)說(shuō),鎖對象就是this //往工作臺上放一盤(pán)菜 if(count >= MAX){ try { //生產(chǎn)者停下來(lái),等待 wait();//默認是this.wait(),所以上面必須加鎖對象synchronized } catch (InterruptedException e) { e.printStackTrace(); } } //上面是安全校驗 count++; System.out.println(Thread.currentThread().getName() + "放了一盤(pán)菜,剩余:" + count); this.notify(); // 包子/工作臺.notify() //喚醒消費者 } //1111111111111111111111111111111111111111111111111111111111111111111111111111 public synchronized void take(){//從工作臺上取走一盤(pán)菜 if(count<=0){ try { wait(); //工作臺沒(méi)有菜,消費者應該停下來(lái) } catch (InterruptedException e) { e.printStackTrace(); } } //上面是安全校驗 count--; System.out.println(Thread.currentThread().getName() + "取走一盤(pán)菜,剩余:" + count); this.notify(); //喚醒生產(chǎn)者 } } //1111111111111111111111111111111111111111111111111111111111111111111111111 class Cook extends Thread{ private Workbench tai; public Cook(String name, Workbench tai) { super(name); this.tai = tai; } public void run(){ while(true){ tai.put(); //封裝了 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } //111111111111111111111111111111111111111111111111111111111111111111111 class Waiter extends Thread{ private Workbench tai; public Waiter(String name, Workbench tai) { super(name); //name屬性在父類(lèi)中已聲明 this.tai = tai; } public void run(){ while(true){ tai.take(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
如下一直交替運行,不停。
如下線(xiàn)程6態(tài):鎖就是鑰匙上廁所,限時(shí)等待就是sleep,記住下面三個(gè)紅色。
如下B進(jìn)不去不執行
到此這篇關(guān)于Java線(xiàn)程創(chuàng )建(賣(mài)票),線(xiàn)程同步(賣(mài)包子)的實(shí)現示例的文章就介紹到這了,更多相關(guān)Java線(xiàn)程創(chuàng )建同步內容請搜索腳本之家以前的文章或繼續瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自本網(wǎng)站內容采集于網(wǎng)絡(luò )互聯(lián)網(wǎng)轉載等其它媒體和分享為主,內容觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如侵犯了原作者的版權,請告知一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容,聯(lián)系我們QQ:712375056,同時(shí)歡迎投稿傳遞力量。
Copyright ? 2009-2022 56dr.com. All Rights Reserved. 特網(wǎng)科技 特網(wǎng)云 版權所有 特網(wǎng)科技 粵ICP備16109289號
域名注冊服務(wù)機構:阿里云計算有限公司(萬(wàn)網(wǎng)) 域名服務(wù)機構:煙臺帝思普網(wǎng)絡(luò )科技有限公司(DNSPod) CDN服務(wù):阿里云計算有限公司 百度云 中國互聯(lián)網(wǎng)舉報中心 增值電信業(yè)務(wù)經(jīng)營(yíng)許可證B2
建議您使用Chrome、Firefox、Edge、IE10及以上版本和360等主流瀏覽器瀏覽本網(wǎng)站