Java多線程知識(shí)點(diǎn)全面總結(jié)
Java多線程知識(shí)點(diǎn)總結(jié)
(1)什么是進(jìn)程?什么是線程?
進(jìn)程:
是并發(fā)執(zhí)行程序在執(zhí)行過程中分配和管理資源的基本單位,當(dāng)程序進(jìn)入內(nèi)存圓形時(shí)即為線程
進(jìn)程三大特點(diǎn):
- 獨(dú)立性: 進(jìn)程是系統(tǒng)中獨(dú)立存在的實(shí)體,它可以獨(dú)立擁有資源,每個(gè)進(jìn)程都有自己的獨(dú)立空間。
- 動(dòng)態(tài)性: 進(jìn)程和程序的區(qū)別在于進(jìn)程動(dòng)態(tài)的,進(jìn)程具有自己的生命周期
- 并發(fā)性:多個(gè)進(jìn)程可以在單個(gè)處理器上并發(fā)執(zhí)行互不影響
并行和并發(fā):
- 并行是指同一時(shí)刻,多個(gè)命令同時(shí)執(zhí)行;
- 并發(fā)是指在同一時(shí)刻,只有一條命令是在處理器上執(zhí)行的,但多個(gè)進(jìn)程命令被快速輪換執(zhí)行,使得在宏觀上具有多個(gè)進(jìn)程同時(shí)執(zhí)行的效果。
線程:
- 線程(Thread)被稱為輕量級(jí)進(jìn)程,線程是進(jìn)程的執(zhí)行單元
- 線程是進(jìn)程的組成部分,一個(gè)進(jìn)程可以擁有多個(gè)線程,而一個(gè)線程必須擁有一個(gè)父進(jìn)程。線程可以擁有堆棧,局部變量…但不能擁有系統(tǒng)資源,它與父進(jìn)程的其他線程共享該進(jìn)程的所有資源
- 一個(gè)線程可與其父進(jìn)程的其他線程共同擁有父進(jìn)程共享變量和部分環(huán)境,共同非合作完成任務(wù)。
- 可以簡便理解一個(gè)Class類執(zhí)行時(shí)就是一個(gè)JVM的進(jìn)程,其中的成員屬性是子線程共享資源,main方法是主線程,其他方法是子線程,子線程(方法)相互合作共同完成Class任務(wù)。
- 線程是獨(dú)立運(yùn)行的,線程的執(zhí)行是搶占式的,也就是說執(zhí)行的線程可能被掛起,以便運(yùn)行另一個(gè)線程。
- 一個(gè)進(jìn)程可以創(chuàng)建或者撤銷另一個(gè)線程,一個(gè)進(jìn)程中的線程是并發(fā)執(zhí)行的。
(2)多線程的運(yùn)行狀態(tài)
(1)線程一般具有5種基本狀態(tài) :
創(chuàng)建, 就緒, 運(yùn)行, 阻塞, 終止
(2)
創(chuàng)建狀態(tài):在程序中
(3)線程的創(chuàng)建和使用
(1)線程的創(chuàng)建
在Java 中如果要想實(shí)現(xiàn)多線程,那么必須依靠線程的主題類,但這個(gè)線程主題類在定義的時(shí)候,也需要一類特殊的要求,這個(gè)類可以繼承 Thread ,實(shí)現(xiàn)Runnable 接口或者實(shí)現(xiàn) Callable 接口來完成定義。任何一個(gè)類只要繼承了 Thread 類就可以成為一個(gè)類的主類,同時(shí)線程中需要覆寫父類終點(diǎn) run( )方法。
(2)線程的使用
public class JavaDemoA { public static void main(String[] args) { new MyThread("線程A:").start(); new MyThread("線程B:").start(); new MyThread("線程C:").start(); } } class MyThread extends Thread{ //線程的主體類 private String title; public MyThread(String title){ this.title=title; } @Override public void run() { for(int i=1;i<=4;i++){ System.out.println(this.title+i); } } }
(4)Runnable 接口實(shí)現(xiàn)多線程
Thread是 Runnable 的子類,繼承了 Runnable 的接口,Thread 類的確可以方便實(shí)現(xiàn)多線程,但這種方式最大的缺陷就是單繼承局限性
Runnable 接口從 JDK1.8 開始成為一個(gè)函數(shù)接口,可以直接利用lambda表達(dá)式來實(shí)現(xiàn)線程主體代碼,同時(shí)在該接口中提供有run()方法進(jìn)行線程功能定義
Thread構(gòu)造方法:public Thread(Runnable target).
在Thread類中會(huì)保存有target屬性,該屬性保存的是Runnable的核心業(yè)務(wù)主體對象
當(dāng)Thread.start() 方法啟動(dòng)多線程時(shí)也會(huì)調(diào)用Thread.run() 方法,而在 Thread.run() 會(huì)判斷是否提供有target實(shí)例,如果提供則調(diào)用真實(shí)主體
public class JavaDemoB { public static void main(String[] args) { new Thread(new MyThreadB("線程A:")).start(); new Thread(new MyThreadB("線程B:")).start(); new Thread(new MyThreadB("線程C:")).start(); } } class MyThreadB implements Runnable{ private String title; public MyThreadB(String title){ this.title=title; } @Override public void run() { for(int i=1;i<=5;i++){ System.out.println(title+i); 加粗樣式 } } }
(5)Callable接口實(shí)現(xiàn)多線程
Runnable 接口實(shí)例化多線程可以避免單繼承問題,但 Runnable 中的run() 方法不能返回操作結(jié)果,為啦解決這樣的問題,從JDK1.5開始對于多線程的實(shí)現(xiàn)提供了一個(gè)新的接口 java.util.concurrent.Callable
Callable接口在定義時(shí)需要定義泛型。
通過FutureTask 實(shí)現(xiàn)Callable接口和Thread類的聯(lián)系,并且通過FutureTask類獲取Callable接口中call()方法的返回值。
import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; public class JavaDemoC { public static void main(String[] args) throws Exception{ FutureTask<String> task=new FutureTask<>(new MyThreadC()); new Thread(task).start(); System.out.println(task.get()); } } class MyThreadC implements Callable<String> { @Override public String call() throws Exception { for(int i=1;i<=5;i++){ System.out.println("線程執(zhí)行:i="+i); } return "Callable實(shí)現(xiàn)多線程"; } }
多線程常用操作方法
(1)線程的命名和取得
public Thread (Runnable target,String name) 構(gòu)造實(shí)例化線程對象,接受 Runnable 接口子類對象,同時(shí)設(shè)置線程名稱;
public final void setName(String name) 普通 設(shè)置線程名稱
public final String getName() 取得線程名字
每當(dāng)實(shí)例化Thread類對象時(shí),都會(huì)調(diào)用init()方法,并且在沒有為線程命名時(shí),自動(dòng)命名。
package ThreadTest; public class JavaDemoD { public static void main(String[] args) { MyThreadD myThread=new MyThreadD(); new Thread(myThread,"線程A").start();//線程手動(dòng)命名 new Thread(myThread).start();//線程自動(dòng)命名 new Thread(myThread,"線程B").start(); } } class MyThreadD implements Runnable{ @Override public void run() { //獲取當(dāng)前線程名稱 System.out.println(Thread.currentThread().getName()); } }
(2)線程休眠
線程有時(shí)候需要減緩執(zhí)行速度,所以Thread 類提供啦sleep()方法
public static void sleep(long millis) throws InterruptedException設(shè)置線程毫秒數(shù),時(shí)間一到自動(dòng)喚醒
public static void sleep(long millis,int nanos) throws InterruptedException設(shè)置線程毫秒數(shù),納秒數(shù),時(shí)間一到自動(dòng)喚醒
public class JavaDemoE { public static void main(String[] args){ Runnable runnable=(()->{//Runnable接口實(shí)例 for(int i=1;i<=5;i++){ System.out.println(Thread.currentThread().getName()+i); try { Thread.sleep(2000);//強(qiáng)制讓線程休眠2秒 } catch (InterruptedException e) { e.printStackTrace(); } } }); new Thread(runnable,"線程A:").start(); new Thread(runnable,"線程B:").start(); } //本程序兩個(gè)線程對象,每一個(gè)線程對象執(zhí)行時(shí)都要休眠一秒,因?yàn)槎嗑€程的啟動(dòng)和執(zhí)行都是有操作系統(tǒng), //隨機(jī)分配,雖然看起來A,B線程同時(shí)休眠,但也有先后順序 }
(3)線程中斷
Thread 中提供的線程方法很多都會(huì)拋出InterruptedException中斷異常,所以線程在執(zhí)行過程中可以被另一個(gè)線程中斷。
public boolean isInterrupted( ) 普通方法 判斷線程是否被中斷
public void interrupt( ) 普通 中斷線程執(zhí)行
public class JavaDemoF { public static void main(String[] args) throws InterruptedException { Thread thread=new Thread(()->{ System.out.println("線程累了想休眠10S"); try { Thread.sleep(10000); System.out.println("時(shí)間到,自然醒來"); } catch (InterruptedException e) { System.out.println("被強(qiáng)制喚醒,繼續(xù)工作"); } }); thread.start();//線程執(zhí)行啟動(dòng) Thread.sleep(2000);//先讓子線程運(yùn)行2秒 if(!thread.isInterrupted()){//如果線程沒有中斷 System.out.println("讓線程終止休眠"); thread.interrupt();//線程中斷 } } }
(4)線程強(qiáng)制執(zhí)行
在多線程并發(fā)執(zhí)行中每個(gè)線程對象都會(huì)交替執(zhí)行,如果某個(gè)線程對象需要優(yōu)先完成執(zhí)行,可以使用 join ()方法強(qiáng)制執(zhí)行,待其執(zhí)行完畢后其他線程才會(huì)繼續(xù)執(zhí)行
public final void join() throws InterruptedException
public class JavaDemoG { public static void main(String[] args) { Thread threadB=new Thread(new MyThreadH(),"線程B"); Runnable runnable=(()->{ for(int i=1;i<=9;i++){ System.out.println(Thread.currentThread().getName()+i); if(i==5){ try { threadB.join();//如果i=5,強(qiáng)制執(zhí)行線程B } catch (InterruptedException e) { e.printStackTrace(); } } } }); new Thread(runnable,"線程A").start(); threadB.start(); } } class MyThreadH implements Runnable{ @Override public void run() { for(int i=1;i<=5;i++){ System.out.println(Thread.currentThread().getName()+i); } } }
(5)線程禮讓
多線程在彼此交替執(zhí)行時(shí)往往需要進(jìn)行資源輪流占用,如果某些不是很重要的線程搶占到資源但又不著急執(zhí)行時(shí)就可以暫時(shí)禮讓出去,讓其他線程先執(zhí)行。
public static void yield( ) 線程禮讓
public class JavaDemoH { public static void main(String[] args) { Runnable runnable=(()->{//Lambda實(shí)例化線程類對象,方便演示 for(int j=1;j<=15;j++){ System.out.println(Thread.currentThread().getName()+j); } }); new Thread(new MyThreadI(),"禮讓線程A").start(); new Thread(runnable,"非禮讓線程B:").start(); new Thread(runnable,"非禮讓線程C:").start(); new Thread(runnable,"非禮讓線程D:").start(); } } class MyThreadI implements Runnable{ @Override public void run() { for(int i=1;i<=10;i++){ System.out.println(Thread.currentThread().getName()+i); if(i%3==0){ Thread.yield();//線程禮讓 } } } }
(6)線程優(yōu)先級(jí)
在java線程操作時(shí),所有線程運(yùn)行前都保持就緒狀態(tài),此時(shí)那個(gè)線程的優(yōu)先級(jí)高就有可能優(yōu)先被執(zhí)行
所有創(chuàng)建的線程都是子線程,啟動(dòng)時(shí)都是同樣的優(yōu)先級(jí)。
主線程是中等優(yōu)先級(jí) 5
public final void setPriority ( int newPriority ) 設(shè)置線程優(yōu)先級(jí)
MAX_PRIORITY 最高優(yōu)先級(jí) 10
NORM_PRIORITY 中等優(yōu)先級(jí) 5
MIN_PRIORITY 最低優(yōu)先級(jí) 1
public final int getPriority ( ) 獲取線程優(yōu)先級(jí)
public class JavaDemoI { public static void main(String[] args) { Runnable runnable=(()->{ for (int i=1;i<=5;i++){ System.out.println(Thread.currentThread().getName()+i); } }); Thread threadA=new Thread(runnable,"線程A"); Thread threadB=new Thread(runnable,"線程B"); Thread threadC=new Thread(runnable,"線程C"); threadA.setPriority(Thread.MIN_PRIORITY); threadB.setPriority(Thread.NORM_PRIORITY); threadC.setPriority(Thread.MAX_PRIORITY); threadA.start(); threadB.start(); threadC.start(); } }
(7)如何停止線程
(1)停止線程的3個(gè)方法(了解)
- 停止多線:public void stop ()
- 掛起多線程::public final void suspend()
- 恢復(fù)掛起的多線程:public final void resume()
(2)對多線程中 stop (), suspend(), resume() 方法在jdk1.2開始不建議使用,主要是因?yàn)檫@三個(gè)方法在操作時(shí)會(huì)產(chǎn)生死鎖的問題。
(3)優(yōu)雅的停止一個(gè)線程
代碼演示:
package ThreadTest; public class JavaDemoO { private static boolean flag=true; public static void main(String[] args) throws InterruptedException { new Thread(()->{ long num=0; while (flag){ try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"正在運(yùn)行"+num++); } },"執(zhí)行線程").start(); Thread.sleep(200); flag=false; } }
(8)后臺(tái)守護(hù)線程
(1)java中的線程分為兩類:
- 用戶線程和守護(hù)線程,守護(hù)線程:是一種運(yùn)行在后臺(tái)的線程服務(wù)線程,當(dāng)用戶線程存在時(shí),守護(hù)線程也可以用時(shí)存在;當(dāng)用戶線程消失時(shí),守護(hù)線程也會(huì)消失。
- public final void setDaemon(boolean on) 設(shè)置為守護(hù)線程
- public final boolean idDaemon() 判斷是否是守護(hù)線程
代碼:
package ThreadTest; public class JavaDemoP { public static void main(String[] args) { Thread threadA=new Thread(()->{ for(int i=1;i<=100;i++){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+i); } },"用戶線程"); Thread threadB=new Thread(()->{ for(int i=1;i<=200;i++){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+i); } },"守護(hù)線程"); threadB.setDaemon(true);//設(shè)置為守護(hù)線程 threadA.start(); threadB.start(); } }
結(jié)果:用戶線程結(jié)束時(shí),守護(hù)線程也結(jié)束
(9)volatile 關(guān)鍵字
在多線程編程中,若干個(gè)線程為了可以實(shí)現(xiàn)公共資源的操作,往往是復(fù)制相應(yīng)變量的副本,待完成操作后再將副本變量的數(shù)據(jù)與原始數(shù)據(jù)進(jìn)行同步處理,如果開發(fā)者不希望通過副本數(shù)據(jù)進(jìn)行操作,而是希望可以直接通過原始變量的操作(節(jié)省了復(fù)制變量副本與同步的時(shí)間),則可以在變量聲明時(shí)使用volatile關(guān)鍵字.
使用volatile關(guān)鍵字
代碼:
package ThreadTest; public class JavaDemoQ { public static void main(String[] args) { new Thread(new MyThreadQ(),"線程A售票成功_剩余票數(shù):").start(); new Thread(new MyThreadQ(),"線程B售票成功_剩余票數(shù):").start(); new Thread(new MyThreadQ(),"線程C售票成功_剩余票數(shù):").start(); new Thread(new MyThreadQ(),"線程D售票成功_剩余票數(shù):").start(); } } class MyThreadQ implements Runnable{ private volatile int ticket=50;//直接內(nèi)存操作 @Override public void run() { while (ticket>0){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } ticket--; System.out.println(Thread.currentThread().getName()+ticket); } } }
結(jié)果:
volatile與synchronized的區(qū)別:
- volatile無法進(jìn)行同步處理操作,它只是一種直接內(nèi)存的處理,避免副本操作
- volatile主要是在屬性上使用,而synchronize是在方法和代碼塊上使用。
線程的同步和死鎖
(1)線程同步問題
(1) 同步問題的引出
public class JavaDemoJ { private static int ticket=5;//總票數(shù)5張 public static void main(String[] args) { Runnable runnable=(()->{ while (true){//持續(xù)賣票 if(ticket>0){ try { Thread.sleep(100);//模擬網(wǎng)絡(luò)延遲 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"賣票成功\t剩余票數(shù):"+ticket--); } if(ticket<=0){ System.out.println(Thread.currentThread().getName()+"賣票失敗\t剩余票數(shù):"+ticket); break; } } }) ; new Thread(runnable,"售票員A:").start(); new Thread(runnable,"售票員B:").start(); new Thread(runnable,"售票員C:").start(); //假設(shè)此時(shí)還有一張票,當(dāng)?shù)谝粋€(gè)線程滿足售票條件時(shí)(還未減少票數(shù)),其他線程也可能滿足售票條件, //就有可能造成同一張票賣出去兩次。 } }
問題所在:
假設(shè)此時(shí)還有一張票,當(dāng)?shù)谝粋€(gè)線程滿足售票條件時(shí)(還未減少票數(shù)),其他線程也可能滿足售票條件就有可能造成同一張票賣出去兩次。
(2) 解決同步問題
Java使用Synchronized關(guān)鍵字實(shí)現(xiàn)同步操作,同步的關(guān)鍵是要給代碼上“鎖”,而對于鎖的操作有兩種:同步代碼塊,同步方法。
(1)同步代碼塊
同步代碼塊是指使用synchronized關(guān)鍵字定義的代碼塊,在代碼執(zhí)行時(shí),往往需要設(shè)置一個(gè)同步對象,由于線程操作的不確定性所以這時(shí)候的同步對象可以選擇 this。
將票數(shù)判斷和票數(shù)自減放在同一代碼塊中,當(dāng)多線程并發(fā)執(zhí)行時(shí),只允許一個(gè)線程執(zhí)行該部分代碼塊,就實(shí)現(xiàn)啦同步處理操作、
public class JavaDemoK { public static void main(String[] args) { MyThreadK myThreadK=new MyThreadK(); new Thread(myThreadK,"線程A售票成功,剩余票數(shù):").start(); new Thread(myThreadK,"線程B售票成功,剩余票數(shù):").start(); new Thread(myThreadK,"線程C售票成功,剩余票數(shù):").start(); new Thread(myThreadK,"線程D售票成功,剩余票數(shù):").start(); } } class MyThreadK implements Runnable{ private static int ticket=10;//總票數(shù) @Override public void run() { while (true){ synchronized (this){//同步代碼塊 if(ticket>0){ try { Thread.sleep(100);//模擬網(wǎng)絡(luò)延遲 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+(--ticket)); }else { System.out.println("全部售空"); break; } } } } }
結(jié)果:
(2)同步方法解決
public class JavaDemoL { public static void main(String[] args) { MyThreadL myThreadL=new MyThreadL(); new Thread(myThreadL,"售票員A售票成功,剩余票數(shù):").start(); new Thread(myThreadL,"售票員B售票成功,剩余票數(shù):").start(); new Thread(myThreadL,"售票員C售票成功,剩余票數(shù):").start(); new Thread(myThreadL,"售票員D售票成功,剩余票數(shù):").start(); } } class MyThreadL implements Runnable{ private static int ticket=10; @Override public void run() { sale(); } public synchronized void sale() {//同步方法 while (true){ if(this.ticket>0){ try { Thread.sleep(100);//網(wǎng)絡(luò)延遲 } catch (InterruptedException e) { e.printStackTrace(); } this.ticket--; System.out.println(Thread.currentThread().getName()+this.ticket); } else { System.out.println("票已售空"); break; } } } }
結(jié)果:
(2)線程死鎖問題
(1)死鎖問題的引出
代碼:
package ThreadTest; public class JavaDemoJ implements Runnable{ private book book=new book(); private money money=new money(); public JavaDemoJ(){ new Thread(this).start(); book.tell(money); } public void run(){ money.tell(book); } public static void main(String[] args) { new JavaDemoJ(); } } class book{ public synchronized void tell(money money){ System.out.println("你先給我錢,我才給你書!"); money.get(); } public synchronized void get(){ System.out.println("收到錢,把書給買家!"); } } class money{ public synchronized void tell(book book){ System.out.println("你先給我書,我再給你錢!"); book.get(); } public synchronized void get(){ System.out.println("收到書,把錢給賣家!"); } }
結(jié)果:
結(jié)論:本程序中采用大量同步處理操作,而死鎖一旦出現(xiàn)線程將進(jìn)入等待操作,并且不會(huì)向下繼續(xù)執(zhí)行。
(3)生產(chǎn)者消費(fèi)者問題
(1)生產(chǎn)者生產(chǎn),消費(fèi)者取出,生產(chǎn)者生產(chǎn)一個(gè),消費(fèi)者取出一個(gè)
未同步代碼:
package ThreadTest; public class JavaDemoM { public static void main(String[] args) { Message message=new Message(); new Thread(new Producer(message)).start(); new Thread(new Consumer(message)).start(); } } class Message{//消息類 private String title;//標(biāo)題 private String content;//內(nèi)容 public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } } class Producer implements Runnable{//定義生產(chǎn)者 private Message msg=null; public Producer(Message msg){ this.msg=msg; } @Override public void run() { for(int i=1;i<=50;i++){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } if(i%2==0){ msg.setTitle(i+"英雄"); msg.setContent("名垂千古"); }else { msg.setTitle(i+"小人"); msg.setContent("遺臭萬年"); } } } } class Consumer implements Runnable{ private Message msg; public Consumer(Message msg){ this.msg=msg; } @Override public void run() { for (int i=1;i<=50;i++){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("【"+msg.getTitle()+","+msg.getContent()+"】"); } } }
錯(cuò)誤產(chǎn)生結(jié)果:
問題分析:
數(shù)據(jù)錯(cuò)位:假設(shè)生產(chǎn)者線程還剛在存儲(chǔ)空間添加一個(gè)數(shù)據(jù)的標(biāo)題,還未添加內(nèi)容,程序就切換到消費(fèi)者線程,消費(fèi)者就會(huì)把沒有生產(chǎn)者沒有添加的內(nèi)容和上一組生產(chǎn)的內(nèi)容聯(lián)系在一起導(dǎo)致數(shù)據(jù)錯(cuò)位。重復(fù)操作:生產(chǎn)者線程放入多組數(shù)據(jù),消費(fèi)者線程才開始取出,或者是消費(fèi)者還沒生產(chǎn)數(shù)據(jù),消費(fèi)者就已經(jīng)重復(fù)取出數(shù)據(jù)。
同步代碼解決:
package ThreadTest; public class JavaDemoN { public static void main(String[] args) { Message2 msg=new Message2(); new Thread(new Producer2(msg)).start(); new Thread(new Consumer2(msg)).start(); } } class Message2 { private String title; private String content; private boolean key=true; //true 允許生產(chǎn)不允許消費(fèi) //false 允許消費(fèi)不允許生產(chǎn) public synchronized void set(String title,String content) {//同步方法 if(this.key==false){//允許消費(fèi)不允許生產(chǎn) try { super.wait();//線程等待 } catch (InterruptedException e) { e.printStackTrace(); } } this.title = title; this.content = content; this.key=false;//生產(chǎn)完畢 super.notify();//喚醒線程 } public synchronized String get() { if(this.key==true){//允許生產(chǎn)不允許消費(fèi) try { super.wait();//線程等待 } catch (InterruptedException e) { e.printStackTrace(); } } try { return title +","+ content; }finally { this.key=true; super.notify(); } } } class Producer2 implements Runnable{//定義生產(chǎn)者 private Message2 msg=null; public Producer2(Message2 msg){ this.msg=msg; } @Override public void run() { for(int i=1;i<=50;i++){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } if(i%2==0){ this.msg.set(i+"英雄","萬古流芳"); }else { this.msg.set(i+"小人","遺臭萬年"); } } } } class Consumer2 implements Runnable{ private Message2 msg; public Consumer2(Message2 msg){ this.msg=msg; } @Override public void run() { for (int i=1;i<=50;i++){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("【"+msg.get()+"】"); } } }
正確執(zhí)行結(jié)果:
原理解釋:
將同步操作設(shè)置在Message類中,使用synchronized關(guān)鍵字可以使消息內(nèi)容同步,不會(huì)造成消息錯(cuò)位。
在生產(chǎn)者和消費(fèi)者線程中根據(jù)key判斷,是否可以生產(chǎn)或者取出,如果不能生產(chǎn)或者取出,可使用 wait( ) 線程等待方法 等待另一方線程取出或者生產(chǎn)后,使用 notify( ) 線程喚醒,繼續(xù)執(zhí)行操作。notifyAll( ) 是喚醒全部等待的線程
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
springboot模塊里面調(diào)用另外一個(gè)模塊的方法實(shí)現(xiàn)
在Spring-Boot項(xiàng)目開發(fā)中,存在著本模塊的代碼需要訪問外面模塊接口,本文就來介紹一下springboot模塊里面調(diào)用另外一個(gè)模塊的方法實(shí)現(xiàn),感興趣的可以了解一下2023-11-11Springboot?上傳文件或頭像(MultipartFile、transferTo)
本文主要介紹了Springboot?上傳文件或頭像(MultipartFile、transferTo),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04深入理解Java設(shè)計(jì)模式之職責(zé)鏈模式
這篇文章主要介紹了JAVA設(shè)計(jì)模式之職責(zé)鏈模式的的相關(guān)資料,文中示例代碼非常詳細(xì),供大家參考和學(xué)習(xí),感興趣的朋友可以了解2021-11-11springboot實(shí)現(xiàn)攔截器之驗(yàn)證登錄示例
本篇文章主要介紹了springboot實(shí)現(xiàn)攔截器之驗(yàn)證登錄示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-02-02解決springcloud 配置gateway 出現(xiàn)錯(cuò)誤的問題
今天給大家分享springcloud 配置gateway 出現(xiàn)錯(cuò)誤的問題,其實(shí)解決方法很簡單,只需要降低springcloud版本,改成Hoxton.SR5就好了,再次改成Hoxton.SR12,也不報(bào)錯(cuò)了,下面給大家展示下,感興趣的朋友一起看看吧2021-11-11