這篇文章主要介紹了Java并發(fā)編程之介紹線程安全基礎(chǔ)的示例,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。
創(chuàng)新互聯(lián)自2013年起,是專業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項目做網(wǎng)站、成都網(wǎng)站建設(shè)網(wǎng)站策劃,項目實施與項目整合能力。我們以讓每一個夢想脫穎而出為使命,1280元猇亭做網(wǎng)站,已為上家服務(wù),為猇亭各地企業(yè)和個人服務(wù),聯(lián)系電話:13518219792
線程安全基礎(chǔ)
1.線程安全問題
2.賬戶取款案例
3.同步代碼塊synchronized
synchronized的理解
java中有三大變量的線程安全問題
在實例方法上使用synchronized
總結(jié)
面試題
4.死鎖
5.開發(fā)中應(yīng)該怎么解決線程安全問題
6.守護線程
7.定時器
8.實現(xiàn)線程的第三種方式:實現(xiàn)Callable接口
9.Object類中的wait和notify方法
10.生產(chǎn)者和消費者
1.線程安全問題
2.1、為什么這個是重點?
??以后在開發(fā)中,我們的項目都是運行在服務(wù)器當中,而服務(wù)器已經(jīng)將線程的定義,線程對象的創(chuàng)建,線程的啟動等,都已經(jīng)實現(xiàn)完了。這些代碼我們都不需要編寫。
??最重要的是:你要知道,你編寫的程序需要放到一個多線程的環(huán)境下運行,你更需要關(guān)注的是這些數(shù)據(jù)在多線程并發(fā)的環(huán)境下是否是安全的。(重點:*****)
2.2、什么時候數(shù)據(jù)在多線程并發(fā)的環(huán)境下會存在安全問題呢?
??三個條件:
??條件1:多線程并發(fā)。
??條件2:有共享數(shù)據(jù)。
??條件3:共享數(shù)據(jù)有修改的行為。
??滿足以上3個條件之后,就會存在線程安全問題。
2.3、怎么解決線程安全問題呢?
當多線程并發(fā)的環(huán)境下,有共享數(shù)據(jù),并且這個數(shù)據(jù)還會被修改,此時就存在線程安全問題,怎么解決這個問題?
??線程排隊執(zhí)行。(不能并發(fā))。
??用排隊執(zhí)行解決線程安全問題。
??這種機制被稱為:線程同步機制。
??專業(yè)術(shù)語叫做:線程同步,實際上就是線程不能并發(fā)了,線程必須排隊執(zhí)行。
??怎么解決線程安全問題呀?
??使用“線程同步機制”。
??線程同步就是線程排隊了,線程排隊了就會犧牲一部分效率,沒辦法,數(shù)據(jù)安全第一位,只有數(shù)據(jù)安全了,我們才可以談效率。數(shù)據(jù)不安全,沒有效率的事兒。
2.4、說到線程同步這塊,涉及到這兩個專業(yè)術(shù)語:
異步編程模型:
??線程t1和線程t2,各自執(zhí)行各自的,t1不管t2,t2不管t1,
??誰也不需要等誰,這種編程模型叫做:異步編程模型。
??其實就是:多線程并發(fā)(效率較高。)
同步編程模型:
??線程t1和線程t2,在線程t1執(zhí)行的時候,必須等待t2線程執(zhí)行結(jié)束,或者說在t2線程執(zhí)行的時候,必須等待t1線程執(zhí)行結(jié)束,兩個線程之間發(fā)生了等待關(guān)系,這就是同步編程模型。效率較低。線程排隊執(zhí)行。
異步就是并發(fā)。同步就是排隊。
2.賬戶取款案例
Account類
package ThreadSafe;public class Account { //賬號 private String actno; //余額 private double balance; public Account(String actno, double balance) { super(); this.actno = actno; this.balance = balance; } public String getActno() { return actno; } public void setActno(String actno) { this.actno = actno; } public double getBalance() { return balance; } public void setBalance(double balance) { this.balance = balance; } //取款的方法 public void withdraw(double money){ //t1和t2并發(fā)執(zhí)行這個方法(t1和t2是兩個棧 ,兩個棧操作堆中同一個對象) //取款之前的余額 double before=this.getBalance(); //取款之后的余額 double after=before-money; //模擬一下網(wǎng)絡(luò)延遲,會出現(xiàn)問題 try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } //更新余額 //思考:t1執(zhí)行到這里了,但還沒有來得及執(zhí)行這行代碼,t2線程進來withdraw方法了,此時一定出問題 this.setBalance(after); }}AccountThread類public class AccountThread extends Thread{ //兩個線程必須共享一個賬戶對象 private Account act; //通過構(gòu)造方法傳遞過來賬戶對象 public AccountThread(Account act) { this.act = act; } @Override public void run() { //假設(shè)取款5000 double money=5000; //多線程執(zhí)行這個方法 act.withdraw(money); System.out.println(Thread.currentThread().getName()+"賬戶"+act.getActno()+"取款成功"+act.getBalance()); }}
Test類
public class Test { public static void main(String[] args) { //創(chuàng)建賬戶對象 Account act=new Account("act-001",10000); //創(chuàng)建兩個線程 Thread t1=new AccountThread(act); Thread t2=new AccountThread(act); t1.setName("t1"); t2.setName("t2"); //啟動兩個線程執(zhí)行 t1.start(); t2.start(); }}
3.同步代碼塊synchronized
synchronized的理解
//以下這幾行代碼必須是線程排隊的,不能并發(fā)
//一個線程把這里的代碼全部執(zhí)行結(jié)束后,另一個線程才能進來
/*
線程同步機制的語法是
synchronized(){
//線程同步代碼塊。
}
synchronized后面小括號中傳的這個數(shù)據(jù)是相當關(guān)鍵的,
這個數(shù)據(jù)必須是多線程共享的數(shù)據(jù),才能達到多線程排隊。
()中寫什么?
那要看你想讓哪些線程同步
假設(shè)t1、t2、t3、t4、t5有5個線程
你只希望t1 t2 t3排隊,t4 t5不需要排隊,怎么辦
你一定要在()中寫一個t1 t2 t3共享的對象,而這個對象對于t4 t5來說不是共享的
這里的共享對象是賬戶對象
賬戶對象是共享的,this是賬戶對象
有時不一定是this,這里只要是多線程共享的那個對象就行
synchronized(this){ double before=this.getBalance(); double after=before-money; try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } this.setBalance(after); }
在java語言中,任何對象都有一把鎖,其實這把鎖就是一個標記,(只是把它叫做鎖)
100個對象,100個鎖,1個對象1把鎖。
以上代碼的執(zhí)行原理是什么呢?
1.假設(shè)t1和t2線程并發(fā),開始執(zhí)行以上代碼的時候,肯定有一個先一個后,
2.假設(shè)t1先執(zhí)行了,遇到了synchronized,這個時候自動找后面共享對象的對象鎖,找到之后,并占有這把鎖,然后執(zhí)行同步代碼塊中的程序,在程序執(zhí)行過程中一直都占有這把鎖,直到同步代碼塊執(zhí)行結(jié)束,這把鎖才會釋放。
3.假設(shè)t1已經(jīng)占有這把鎖,此時t2也遇到synchronized關(guān)鍵字,也會去占有后面共享對象的這把鎖,結(jié)果這把鎖被t1占有,t2只能在同步代碼塊外邊等待t1的結(jié)束,直到t1把同步代碼塊執(zhí)行結(jié)束了,t1會歸還這把鎖,此時t2終于等到這把鎖,然后t2占有這把鎖之后,進入同步代碼塊執(zhí)行程序。
這樣就達到了線程排隊執(zhí)行
這里需要注意的是:這個共享對象一定要選好了,這個共享對象一定是你需要排隊執(zhí)行的這些線程對象所共享的。
//對象
Object obj=new Object(); //實例變量(Account對象是多線程共享的,Account對象中的實例變量obj也是共享的)
synchronized(obj){}
括號里邊只要是共享對象就行。
Object obj2=new Object(); //局部變量
synchronized(obj2){}
這樣寫就不安全了,因為obj2是局部變量,不是共享對象。
synchronized(“abc”){}
這樣寫時可以的。存在字符串常量池中
寫"abc"的話所有線程都會同步
而如果是寫synchronized(this){}的話,我們創(chuàng)建了一個新的對象act2可不用共享對象。
所以最好是寫synchronized(this){},比如你要取款,要讓其他取別的賬戶的人也要等嗎?不應(yīng)該,只有同時對你這1個賬戶取款的時候,需要等待,別人取錢的時候,需要從其他賬戶中取錢,就不需要等待。
java中有三大變量的線程安全問題
實例變量,在堆中
靜態(tài)變量,在方法區(qū)
局部變量,在棧中
以上三大變量
局部變量永遠不會存在線程安全問題
因為局部變量不共享(一個線程一個棧)
局部變量在棧中,所以局部變量永遠都不會共享
實例變量在堆中,堆只有1個。
靜態(tài)變量在方法區(qū)中,方法區(qū)只有1個。
堆和方法區(qū)都是多線程共享的,所以可能存在線程安全問題。
局部變量+常量:不會有線程安全問題。
成員變量:可能會有線程安全問題。
同步代碼塊越小,效率越高。
//多線程執(zhí)行這個方法//synchronized(this)//這里的this是AccountThread對象,這個對象不共享。synchronized(act){ act.withdraw(money); System.out.println(Thread.currentThread().getName()+"賬戶"+act.getActno()+"取款成功,余額:"+act.getBalance());}
在實例方法上使用synchronized
在實例方法上可以使用synchronized嗎?可以的。
synchronized出現(xiàn)在實例方法上,一定鎖的是this
沒得挑,只能是this,不能是其他的對象了
所以這種方式不靈活。
另外還有一個缺點:synchronized出現(xiàn)在實例方法上,表示整個方法體都需要同步
可能會擴大同步的范圍,導(dǎo)致程序的執(zhí)行效率降低。所以這種方式不常用。
synchronized使用在實例方法上有什么優(yōu)點?
代碼寫的少了,節(jié)儉了。
如果共享的對象是this,并且需要同步的代碼是整個方法體,建議使用這種方式。
StringBuffer就是在每個方法上加了synchronized關(guān)鍵字
使用局部變量的話,最好使用StringBuilder
因為局部變量不存在線程安全問題,選擇StringBuilder,StringBuffer效率比較低。
ArrayList是非線程安全的。
Vector是線程安全的。
HashMap HashSet是非線程安全的。
Hashtable是線程安全的。
總結(jié)
synchronized有三種寫法:
??第一種:同步代碼塊
????靈活
??????synchronized(線程共享對象){
????????同步代碼塊;
??????}
??第二種:在實例方法上使用synchronized
??????表示共享對象一定是this
??????并且同步代碼塊是整個方法體。
??第三種:在靜態(tài)方法上使用synchronized
??????表示找類鎖。
??????類鎖永遠只有1把。
??????就算創(chuàng)建了100個對象,那類鎖也只有一把。
對象鎖:1個對象1把鎖,100個對象100把鎖。
類鎖:100個對象,也可能只是1把類鎖。
面試題
//面試題:doother方法的執(zhí)行需不需要等待dosome方法的結(jié)束。
//不需要,因為doother方法沒有synchronized
public class exam01 { public static void main(String[] args) { MyClass mc=new MyClass(); Thread t1=new MyThread(mc); Thread t2=new MyThread(mc); t1.setName("t1"); t2.setName("t2"); t1.start(); try { Thread.sleep(1000); //這個睡眠的作用是:為了保證t1線程先執(zhí)行 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } t2.start(); }}class MyThread extends Thread{ private MyClass mc; public MyThread(MyClass mc) { super(); this.mc = mc; } public void run(){ if(Thread.currentThread().getName().equals("t1")){ mc.dosome(); } if(Thread.currentThread().getName().equals("t2")){ mc.doOther(); } } }class MyClass{ public synchronized void dosome(){ System.out.println("doSome begin"); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("doSome end"); } public void doOther(){ System.out.println("doOther begin"); System.out.println("doOther end"); }}
當在doother上面加了synchronized呢
//面試題:doother方法的執(zhí)行需不需要等待dosome方法的結(jié)束。
//需要,因為doother方法沒有synchronized
public synchronized void doOther(){ System.out.println("doOther begin"); System.out.println("doOther end"); }
//面試題:doother方法的執(zhí)行需不需要等待dosome方法的結(jié)束。
//不用排隊,誰也不用管誰
MyClass mc1=new MyClass();MyClass mc2=new MyClass();Thread t1=new MyThread(mc1);Thread t2=new MyThread(mc2);
//面試題:doother方法的執(zhí)行需不需要等待dosome方法的結(jié)束。
//需要,因為靜態(tài)方法是類鎖,類鎖不管創(chuàng)建了幾個對象,類鎖只有一把
MyClass mc1=new MyClass();MyClass mc2=new MyClass();Thread t1=new MyThread(mc1);Thread t2=new MyThread(mc2);public synchronized static void dosome(){ System.out.println("doSome begin"); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("doSome end"); } public synchronized static void doOther(){ System.out.println("doOther begin"); System.out.println("doOther end"); }
這種鎖叫排他鎖:
4.死鎖
synchronized在開發(fā)中最好不要嵌套使用,一不小心就可能導(dǎo)致死鎖。
死鎖代碼要會寫。
一般面試官要求你會寫
只有會寫的,才會在以后的開發(fā)中注意這個事兒
因為死鎖很難調(diào)試。
public class DeadLock { public static void main(String[] args) { Object o1=new Object(); Object o2=new Object(); //t1線程和t2線程共享o1,o2 Thread t1=new MyThread1(o1,o2); Thread t2=new MyThread2(o1,o2); t1.start(); t2.start(); }}class MyThread1 extends Thread{ Object o1; Object o2; public MyThread1(Object o1,Object o2){ this.o1=o1; this.o2=o2; } @Override public void run() { synchronized (o1) { try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } synchronized (o2) { } } }}class MyThread2 extends Thread{ Object o1; Object o2; public MyThread2(Object o1,Object o2){ this.o1=o1; this.o2=o2; } @Override public void run() { synchronized (o2) { try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } synchronized (o1) { } } }}
5.開發(fā)中應(yīng)該怎么解決線程安全問題
聊一聊,我們以后開發(fā)中應(yīng)該怎么解決線程安全問題?
??是一上來就選擇線程同步嗎?synchronized
??不是,synchronized會讓程序的執(zhí)行效率降低,用戶體驗不好。
??系統(tǒng)的用戶吞吐量降低。用戶體驗差。在不得已的情況下再選擇
??線程同步機制。
??第一種方案:盡量使用局部變量代替“實例變量和靜態(tài)變量”。
??第二種方案:如果必須是實例變量,那么可以考慮創(chuàng)建多個對象,這樣實例變量的內(nèi)存就不共享了。(一個線程對應(yīng)1個對象,100個線程對應(yīng)100個對象,對象不共享,就沒有數(shù)據(jù)安全問題了。)
??第三種方案:如果不能使用局部變量,對象也不能創(chuàng)建多個,這個時候就只能選擇synchronized了。線程同步機制。
線程這塊還有那些內(nèi)容呢?列舉一下
7.1、守護線程
7.2、定時器
7.3、實現(xiàn)線程的第三種方式:FutureTask方式,實現(xiàn)Callable接口。(JDK8新特性。)
7.4、關(guān)于Object類中的wait和notify方法。(生產(chǎn)者和消費者模式!)
6.守護線程
守護線程
java語言中線程分為兩大類:
??一類是:用戶線程
??一類是:守護線程(后臺線程)
其中具有代表性的就是:垃圾回收線程(守護線程)。
??守護線程的特點:
??一般守護線程是一個死循環(huán),所有的用戶線程只要結(jié)束,
??守護線程自動結(jié)束。
??注意:主線程main方法是一個用戶線程。
??守護線程用在什么地方呢?
??每天00:00的時候系統(tǒng)數(shù)據(jù)自動備份。
??這個需要使用到定時器,并且我們可以將定時器設(shè)置為守護線程。
??一直在那里看著,沒到00:00的時候就備份一次。所有的用戶線程如果結(jié)束了,守護線程自動退出,沒有必要進行數(shù)據(jù)備份了。
package testThread;/* 實現(xiàn)守護線程 * */ public class ThreadTest13 { public static void main(String[] args) { // TODO Auto-generated method stub Thread t=new BakDataThread(); t.setName("備份數(shù)據(jù)的線程"); //啟動之前,將線程設(shè)置為守護線程 t.setDaemon(true); t.start(); //主線程:主線程是用戶線程 for(int i=0;i<10;i++){ System.out.println(Thread.currentThread().getName()+"---->"+i); try { Thread.sleep(1000); } catch (Exception e) { // TODO: handle exception } } }}class BakDataThread extends Thread{ @Override public void run() { int i=0; //即使是死循環(huán),但由于該線程是守護者,當用戶線程結(jié)束,守護線程自動終止 while(true){ System.out.println(Thread.currentThread().getName()+"---->"+(++i)); try { Thread.sleep(1000); } catch (Exception e) { // TODO: handle exception } } }}
7.定時器
定時器的作用:
??間隔特定的時間,執(zhí)行特定的程序。
??每周要進行銀行賬戶的總賬操作。
??每天要進行數(shù)據(jù)的備份操作。
??在實際的開發(fā)中,每隔多久執(zhí)行一段特定的程序,這種需求是很常見的,
那么在java中其實可以采用多種方式實現(xiàn):
??可以使用sleep方法,睡眠,設(shè)置睡眠時間,沒到這個時間點醒來,執(zhí)行任務(wù)。這種方式是最原始的定時器。(比較low)
??在java的類庫中已經(jīng)寫好了一個定時器:java.util.Timer,可以直接拿來用。不過,這種方式在目前的開發(fā)中也很少用,因為現(xiàn)在有很多高級框架都是支持定時任務(wù)的。
??在實際的開發(fā)中,目前使用較多的是Spring框架中提供的SpringTask框架,這個框架只要進行簡單的配置,就可以完成定時器的任務(wù)。
8.實現(xiàn)線程的第三種方式:實現(xiàn)Callable接口
實現(xiàn)線程的第三種方式:實現(xiàn)Callable接口。(JDK8新特性。)
這種方式實現(xiàn)的線程可以獲取線程的返回值。
之前講解的那兩種方式是無法獲取線程返回值的,因為run方法返回void。
思考:
系統(tǒng)委派一個線程去執(zhí)行一個任務(wù),該線程執(zhí)行完任務(wù)之后,可能會有一個執(zhí)行結(jié)果,我們怎么能拿到這個執(zhí)行結(jié)果呢?
使用第三種方式:實現(xiàn)Callable接口方式。
public class ThreadTest14 { public static void main(String[] args) throws Exception, ExecutionException { //第一步:創(chuàng)建一個未來任務(wù)類對象 //參數(shù)非常重要,需要給一個callable接口的實現(xiàn)類對象 FutureTask task=new FutureTask(new Callable(){ @Override //call方法相當于是run方法,只不過這個有返回值,線程執(zhí)行一個任務(wù),執(zhí)行之后可能會有一個執(zhí)行結(jié)果。 public Object call() throws Exception { System.out.println("call method begin"); Thread.sleep(1000); System.out.println("call method begin"); int a=100; int b=200; return a+b; //自動裝箱 } }); //創(chuàng)建線程對象 Thread t=new Thread(task); //啟動線程 t.start(); //這里是main方法,這是在主線程中 //在線程中,怎么獲取t線程的執(zhí)行結(jié)果 //get方法的執(zhí)行會導(dǎo)致當前線程阻塞 Object obj=task.get(); System.out.println("線程執(zhí)行結(jié)果"+obj); //main方法這里的程序要想執(zhí)行必須等待get()方法的結(jié)束 //而get方法可能需要很久。因為get()方法是為了拿另一個線程的執(zhí)行結(jié)果。 //另一個線程的執(zhí)行是需要時間的 System.out.println("hello,world"); }}
這種方式的優(yōu)點:可以獲取到線程的執(zhí)行結(jié)果
這種方式的缺點:效率比較低,在獲取t線程執(zhí)行結(jié)果的時候,當前線程受阻塞,效率較低。
9.Object類中的wait和notify方法
(生產(chǎn)者和消費者模式?。?br/>??第一:wait和notify方法不是線程對象的方法,是java中任何一個java對象都有的方法,因為這兩個方式是Object類中自帶的。
??wait方法和notify方法不是通過線程對象調(diào)用,
??不是這樣的:t.wait(),也不是這樣的:t.notify()…不對。
??第二:wait()方法作用?
????Object o = new Object();
????o.wait();
??表示:
??讓正在o對象上活動的線程進入等待狀態(tài),無期限等待,直到被喚醒為止。
??o.wait();方法的調(diào)用,會讓“當前線程(正在o對象上活動的線程)”進入等待狀態(tài)。
??第三:notify()方法作用?
????Object o = new Object();
????o.notify();
??表示:
??喚醒正在o對象上等待的線程。
??還有一個notifyAll()方法:
??這個方法是喚醒o對象上處于等待的所有線程。
10.生產(chǎn)者和消費者
1.使用wait方法和notify方法實現(xiàn)生產(chǎn)者和消費者模式
2.什么是生產(chǎn)者和消費者模式?
生產(chǎn)線程負責生產(chǎn),消費線程負責消費
生產(chǎn)線程和消費線程要達到均衡
這是一種特殊的業(yè)務(wù)需求,在這種特殊的情況下需要使用wait方法和notify方法
3.wait和notify方法不是線程對象的方法,是普通java對象都有的方法
4.wait方法和notify方法是建立在線程同步的基礎(chǔ)之上。因為多線程要同時操作一個倉庫,有線程安全問題
5.wait方法作用:o.wait()讓正在o對象上活動的線程t進入等待狀態(tài),并且釋放掉t線程之前占有的o對象的鎖
6.notify方法的作用:o.notify()讓正在o對象上等待的線程喚醒,只是5通知,不會釋放o對象上之前占有的鎖
7.模擬這樣一個需求:
倉庫我們采用list集合
list集合中假設(shè)只能存儲1個元素
1個元素就表示倉庫滿了
如果list集合中的元素個數(shù)是0,就表示倉庫空了。
保證list集合中永遠都是最多存儲1個元素
必須做到這種效果,生產(chǎn)1個消費1個。
public class ThreadTest15 { public static void main(String[] args) { //創(chuàng)建一個倉庫獨享,共享的 List list=new ArrayList(); //創(chuàng)建兩個線程對象 //生產(chǎn)者線程 Thread t1=new Thread(new Producer(list)); //消費者線程 Thread t2=new Thread(new Consumer(list)); t1.setName("生產(chǎn)者線程"); t2.setName("消費者線程"); t1.start(); t2.start(); }}//生產(chǎn)線程class Producer implements Runnable{ //倉庫 private List list; public Producer(List list) { this.list = list; } public void run() { //一直生產(chǎn) while(true){ //給倉庫對象list加鎖 synchronized (list) { if(list.size()>0){ //大于0說明倉庫中已經(jīng)有1個元素了 //當前線程進入等待狀態(tài),并且釋放list集合的鎖 try { list.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } //程序能夠執(zhí)行到這里說明倉庫是空的,可以生產(chǎn) Object obj =new Object(); list.add(obj); System.out.println(Thread.currentThread().getName()+"---->"+obj); //喚醒消費者進行消費 list.notifyAll(); } } }}//消費線程class Consumer implements Runnable{ //倉庫 private List list; public Consumer(List list) { this.list = list; } public void run() { //一直消費 while(true){ //給倉庫對象list加鎖 synchronized (list) { if(list.size()==0){ //倉里已經(jīng)空了 try { //倉庫已經(jīng)空了 //消費者線程等待,并釋放掉list集合鎖 list.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } Object obj=list.remove(0); System.out.println(Thread.currentThread().getName()+"---->"+obj); //喚醒生產(chǎn)者生產(chǎn) list.notifyAll(); } }}
感謝你能夠認真閱讀完這篇文章,希望小編分享的“Java并發(fā)編程之介紹線程安全基礎(chǔ)的示例”這篇文章對大家有幫助,同時也希望大家多多支持創(chuàng)新互聯(lián),關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,更多相關(guān)知識等著你來學(xué)習!
分享文章:Java并發(fā)編程之介紹線程安全基礎(chǔ)的示例
標題網(wǎng)址:http://muchs.cn/article24/joocce.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供服務(wù)器托管、做網(wǎng)站、用戶體驗、響應(yīng)式網(wǎng)站、定制開發(fā)、Google
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)