日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關咨詢
選擇下列產(chǎn)品馬上在線溝通
服務時間:8:30-17:00
你可能遇到了下面的問題
關閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
Java多線程小記,你學會了嗎?

一、線程

在一個進程中可以有多個執(zhí)行單元同時運行,來同時完成一個或者多個程序任務,這些執(zhí)行單元被稱為線程。當啟動一個java程序系統(tǒng)就會創(chuàng)建一個進程,該進程也會創(chuàng)建一個線程來運行main方法中的代碼。操作系統(tǒng)中的進程都至少有一個線程。

創(chuàng)新互聯(lián)是一家專注于網(wǎng)站建設、成都做網(wǎng)站與策劃設計,順義網(wǎng)站建設哪家好?創(chuàng)新互聯(lián)做網(wǎng)站,專注于網(wǎng)站建設10多年,網(wǎng)設計領域的專業(yè)建站公司;建站業(yè)務涵蓋:順義等地區(qū)。順義做網(wǎng)站價格咨詢:028-86922220

代碼按照調(diào)用順序依次往下執(zhí)行不會出現(xiàn)代碼交替運行的就叫做單線程程序,實現(xiàn)代碼交替運行效果的叫做多線程程序。多線程程序運行時每個線程之間都是獨立的,可以并發(fā)執(zhí)行。雖然稱可以并發(fā)執(zhí)行但是實際上線程和進程一樣都是由CPU控制輪流執(zhí)行的,只是CPU的速度快讓人感覺是同時執(zhí)行的感覺。所以說多線程交替執(zhí)行代碼。

二、創(chuàng)建線程

java對多線程的支持主要有三種方法:

  1. 繼承Thread類,重寫run方法。(看起來和python的重寫Threading挺像)
  2. 實現(xiàn)Runnable接口,重寫run方法。
  3. 實現(xiàn)Callable接口,重寫call方法并使用Future可以獲取call方法的返回值。

1、Thread類

Thread位于 java.lang包。

優(yōu)勢:代碼簡單

缺陷:一個類只能繼承一個父類,不利于代碼拓展,不能獲取線程的返回值。

主要步驟:

  1. 創(chuàng)建類并繼承Thread,同時重寫run方法。
  2. 創(chuàng)建子類的對象,調(diào)用start方法啟動線程

代碼示例:

/* MyThread.java */
publicclassMyThreadextendsThread{ //繼承Thread
Stringname="";
publicMyThread(Stringname){
this.name=name;
}
publicvoidrun(){ //重寫run方法,run方法的代碼就是每個線程要執(zhí)行的內(nèi)容。
for(inti=1;i<=5;i++){
System.out.println("[+]> "+this.name+" <[+]");
}
}
}

/* TestThread.java */
publicclassTestThread{
publicstaticvoidmain(String[] args) {
MyThreadt1=newMyThread("t1");
t1.start();
MyThreadT2=newMyThread("T2");
T2.start();

}
}

/* 輸出
[+]> t1 <[+]
[+]> T2 <[+]
[+]> t1 <[+]
[+]> T2 <[+]
[+]> t1 <[+]
[+]> T2 <[+]
[+]> t1 <[+]
[+]> T2 <[+]
[+]> t1 <[+]
[+]> T2 <[+]
*/
//輸出的順序并沒有按照我們寫代碼的順序。

從輸出內(nèi)容看有兩個線程在交互運行,但實際上運行這段代碼之后會產(chǎn)生一個!進程 !,一個java的進程,這個java進程里面包含三個線程,其中兩個就是我們定義的t1、T2,還有一個由main方法開啟的主線程,只不過啟動了兩個線程實例時候沒有做其他動作。

2、Runnable接口

優(yōu)勢:代碼簡單,不用繼承。

缺陷:不能獲取線程的返回值。

主要步驟:

  1. 創(chuàng)建Runnable接口的實現(xiàn)類并重寫run方法
  2. 創(chuàng)建Runnable接口實現(xiàn)類的對象
  3. 使用Thread類創(chuàng)建線程實例,并傳入Runnable接口實現(xiàn)類的對象
  4. 調(diào)用Thread實例的run方法

代碼示例:

/* MyThread.java */
publicclassMyThreadimplementsRunnable{
Stringname="";
publicMyThread(Stringname){
this.name=name;
}
publicvoidrun(){
for(inti=1;i<=5;i++){
System.out.println("[+]> "+this.name+" <[+]");
}
}
}
/* TestThread.java */
publicclassTestThread{
publicstaticvoidmain(String[] args) {
MyThreadr1=newMyThread("t1");
MyThreadr2=newMyThread("t2");
Threadt1=newThread(r1);
Threadt2=newThread(r2);
t1.start();
t2.start();
}
}
/* 輸出
[+]> t1 <[+]
[+]> t2 <[+]
[+]> t1 <[+]
[+]> t2 <[+]
[+]> t1 <[+]
[+]> t2 <[+]
[+]> t1 <[+]
[+]> t2 <[+]
[+]> t1 <[+]
[+]> t2 <[+]
*/

因為run方法是Runnable接口唯一的抽象方法,Runnable就屬于函數(shù)式接口,可以使用Lambda表達式來實現(xiàn)Runnable的線程實例。

/* TestThread */
publicclassTestThread{
publicstaticvoidmain(String[] args) {
Runnabler3=()->{
for(inti=1;i<=5;i++){
System.out.println("[+]> t3 <[+]");
}
};
Threadt3=newThread(r3);
t3.start();
}
}

3、Callable接口

優(yōu)勢:代碼簡單,不用繼承,有返回值

主要步驟:

  1. 創(chuàng)建Callable接口實現(xiàn)類,并重寫Callable接口的call方法
  2. 創(chuàng)建Callable接口實現(xiàn)類的對象
  3. 使用FutureTask類的有參構(gòu)造方法封裝Callable接口實現(xiàn)類對象
    FutureTask類:
    Callable接口實現(xiàn)多線程時靠FutureTask類來封裝和管理返回值。FutureTask的父接口是RunnableFuture,是Runnable和Future的結(jié)合。FutureTask實現(xiàn)RunnableFuture接口,RunnableFuture接口又繼承Runnable接口和Future接口。所以FutureTask本質(zhì)是Runnable接口和Future接口的實現(xiàn)類。
    獲取返回值:
Vget() //等待計算完成,然后檢索其結(jié)果,這個會方法會發(fā)生阻塞,直到任務執(zhí)行完畢才會返回。
Vget(longtimeout, TimeUnitunit) //在指定時間內(nèi)獲取執(zhí)行結(jié)果。 指定時間內(nèi)未取到結(jié)果就返回null
  1. 傳入FutureTask對象創(chuàng)建Thread線程實例
  2. 調(diào)用Thread線程實例start方法

代碼示例:

/* MyThread.java */
importjava.util.concurrent.*;
// 1-創(chuàng)建Callable實現(xiàn)類并重寫call方法
publicclassMyThreadimplementsCallable{
Stringname="";
publicMyThread(Stringname){
this.name=name;
}
publicObjectcall() throwsException{
intj=0;
for(inti=1;i<=5;i++){
j=j+i;
System.out.println("[+]> "+this.name+" <[+]");
}
returnj;
}
}

/* TestThread.java */
importjava.util.concurrent.*;
publicclassTestThread{
publicstaticvoidmain(String[] args) throwsException{
MyThreadm1=newMyThread("m1");
MyThreadm2=newMyThread("m2");
FutureTaskft1=newFutureTask<>(m1);
FutureTaskft2=newFutureTask<>(m2);
Threadt1=newThread(ft1);
Threadt2=newThread(ft2);
t1.start();
System.out.println("t1返回: "+ft1.get());
t2.start();
System.out.println("t2返回: "+ft2.get());
}
}

Callable接口實現(xiàn)類和Runnable接口一樣都要使用Thread類來實現(xiàn)多線程,不同的是,Callable傳入的是!Runnable的子類 !FutureTask的實例對象,而我們在FutureTaskft1 = new FutureTask<>(m1);這一步時把Callable接口實現(xiàn)類給封裝進來,這樣Callable接口實現(xiàn)類就可以實現(xiàn)返回值了。

4、總結(jié)

盡量采用Runnable或者Callable來實現(xiàn)多線程操作。因為相對于Thread,Runnable和Callable有以下幾點優(yōu)勢:

  1. 避免java單繼承機制的局限性。
  2. Runnable和Callable更合適處理一個共享資源的情況,把線程和程序的代碼、數(shù)據(jù)有效分離。
    舉例說明:
    有5張票在售類比一個被多線程共享的資源,一共有兩個售票窗口類比多線程,使用Thread會出現(xiàn)以下情況:
/* MyThread.java */
publicclassMyThreadextendsThread{
privateintts=5;
Stringname="";
publicMyThread(Stringname) {
this.name=name;
}
@Override
publicvoidrun() {
while(this.ts>0){
System.out.println(this.name+" ===> 在賣第 "+this.ts+" 張票");
this.ts--;
}
}
}
/* TestThread.java */
publicclassTestThread{
publicstaticvoidmain(String[] args) {
Threadt1=newMyThread("窗口AAAAA");
Threadt2=newMyThread("窗口VVVVV");
t1.start();
t2.start();
}
}
/* 輸出
窗口AAAAA ===> 在賣第 5 張票
窗口VVVVV ===> 在賣第 5 張票
窗口AAAAA ===> 在賣第 4 張票
窗口VVVVV ===> 在賣第 4 張票
窗口AAAAA ===> 在賣第 3 張票
窗口VVVVV ===> 在賣第 3 張票
窗口AAAAA ===> 在賣第 2 張票
窗口VVVVV ===> 在賣第 2 張票
窗口AAAAA ===> 在賣第 1 張票
窗口VVVVV ===> 在賣第 1 張票
*/

每張票都被賣了兩次,這顯然不合理。如果使用Runnable或者Callable,就可以使用同一個實現(xiàn)類創(chuàng)建兩個Thread線程實例,二者訪問的都是同一個資源就不會出現(xiàn)Thread的情況。

publicclassMyThreadimplementsRunnable{
privateintts=5;
@Override
publicvoidrun() {
while(this.ts>0){
System.out.println(Thread.currentThread().getName()+" ===> 在賣第 "+this.ts+" 張票");
this.ts--;
}
}
}
publicclassTestThread{
publicstaticvoidmain(String[] args) {
MyThreadm1=newMyThread();
// Thread(Runnable target, String name) 分配一個新的Thread對象。name是自定義的線程名。
Threadt1=newThread(m1,"窗口AAAAA");
Threadt2=newThread(m1,"窗口VVVVV");
t1.start();
t2.start();
}
}

/* 輸出
窗口AAAAA ===> 在賣第 5 張票
窗口VVVVV ===> 在賣第 5 張票
窗口AAAAA ===> 在賣第 4 張票
窗口VVVVV ===> 在賣第 3 張票
窗口AAAAA ===> 在賣第 2 張票
窗口VVVVV ===> 在賣第 1 張票
*/

三、后臺線程

對于java程序而言,只要有一個前臺線程在運行那么這個進程就不會結(jié)束,相反的如果一個進程只有后臺線程運行,那么這個進程就會結(jié)束。新創(chuàng)建的線程默認都是前臺線程,如果在某個線程啟動(調(diào)用start方法)之前調(diào)用setDaemon(true)語句,就可以把這個線程設置為后臺線程。

代碼示例:

publicclassMyThreadextendsThread{
@Override
publicvoidrun() {
while(true){
System.out.println("這里是thread線程");
}
}
}

publicclassTestThread{
publicstaticvoidmain(String[] args) {
System.out.println("main方法的主線程是否為后臺線程 : "+Thread.currentThread().isDaemon());
Threadt1=newMyThread();
System.out.println("子線程他t1是否為后臺線程 : "+t1.isDaemon());
t1.setDaemon(true);
System.out.println("子線程他t1是否為后臺線程 : "+t1.isDaemon());
t1.start();
}
}
/*輸出
main方法的主線程是否為后臺線程 : false
子線程他t1是否為后臺線程 : false
子線程他t1是否為后臺線程 : true
這里是thread線程
這里是thread線程
這里是thread線程
這里是thread線程
這里是thread線程
這里是thread線程
這里是thread線程
這里是thread線程
*/

四、線程的生命周期

1、NEW 新建狀態(tài)

和其他java對象一樣,由jvm分配了內(nèi)存,還是不能運行,沒有表現(xiàn)出任何線程的動態(tài)特征。

2、RUNNABLE 可運行

新建狀態(tài)下的線程對象調(diào)用start方法。內(nèi)部細分為兩種,線程可以在二者之間相互轉(zhuǎn)換。

READY 就緒:線程對象調(diào)用start方法之后等待JVM調(diào)度,并未運行。

RUNNING 運行:獲得JVM調(diào)度,如果由多個CPU就允許多個線程并行運行。

3、BLOCKED 阻塞

處于運行狀態(tài)的線程失去CPU執(zhí)行權(quán)從而暫停運行進入阻塞狀態(tài),此時JVM不會給它分配CPU,直到進入就緒狀態(tài)。

線程一般在以下情況會阻塞:

  • 線程A運行中試圖獲取同步鎖時,卻被線程B獲取,此時JVM把A存到對象的鎖池,A進入阻塞。
  • 線程運行過程中發(fā)出I/O請求時

4、WATING 等待

處于運行的線程調(diào)用了無時間參數(shù)限制的方法(wait、join...)就進入等待狀態(tài)。處于等待的線程不能爭奪CPU使用權(quán),必須等待其他線程執(zhí)行特定操作之后才可以繼續(xù)爭奪cpu使用。

5、TIMED_WAITING 定時等待

運行線程調(diào)用了有時間參數(shù)限制的方法(sleep...),處于定時的等待的線程也不能立即爭奪CPU使用權(quán)。

6、TERMINATED 終止

線程的run、call方法正常執(zhí)行完畢或者線程拋出一個未捕獲的異常、錯誤,都會導致線程進入終止。進入終止之后就沒有運行資格,不能轉(zhuǎn)換到其他狀態(tài),聲明周期結(jié)束。

五、線程調(diào)度

定義:程序中的多線程時并發(fā)執(zhí)行的,卻不是在統(tǒng)一時間執(zhí)行的。若想被執(zhí)行就需要獲得CPU使用權(quán)。JVM會按照特定的機制為程序中的每個線程分配CPU使用權(quán),這種機制叫線程的調(diào)度。

分時調(diào)度模型:讓所有線程輪流獲得cpu使用權(quán),平均分配每個線程占用cpu的時間篇。

搶占式調(diào)度模型:讓可運行池中所有就緒的線程爭奪cpu使用權(quán)。優(yōu)先級高的線程獲取cpu使用權(quán)的概率大于優(yōu)先級低的線程。

JVM默認采用搶占式調(diào)度模型。

5.1、線程的優(yōu)先級

對線程進行調(diào)度最簡單的方式就是設置線程的優(yōu)先級。線程優(yōu)先級使用1~10之間的整數(shù)表示,數(shù)字越大優(yōu)先級越高。還可以使用Thread類中的三個靜態(tài)常量表示:

static int MAX_PRIORITY   //最高級,=10
static int MIN_PRIORITY //最低級,=1
static int NORM_PRIORITY //普通級,=5,main方法就是普通優(yōu)先級。

修改線程的優(yōu)先級

setPriority(int newPriority)

代碼示例:

public class TestThread {
public static void main(String[] args) {
Thread t1 =new Thread(()->{
for (int i=0;i<=5;i++){
System.out.println("高級輸出: "+i);
}
});

Thread t2 = new Thread(()->{
for (int i=0;i<=5;i++){
System.out.println("低級輸出: "+i);
}
});

t1.setPriority(10);
t2.setPriority(5);
t2.start();
t1.start();
}
}

5.2、sleep 線程休眠

Thread類提供了一個靜態(tài)方法sleep,可以讓線程進入定時等待。sleep方法會拋出InterruptedException。

代碼示例:

public class TestThread {
public static void main(String[] args) throws Exception{
Thread t1 =new Thread(()->{
for (int i=0;i<=5;i++){
//使用異常處理調(diào)用sleep并捕獲異常
if (i==2){
try{
Thread.sleep(500);
}catch (InterruptedException e){
e.printStackTrace();
}
}
System.out.println("高級輸出: "+i);
}
});

Thread t2 = new Thread(()->{
for (int i=0;i<=5;i++){
System.out.println("低級輸出: "+i);
}
});

t1.setPriority(10);
t2.setPriority(2);
t2.start();
t1.start();
}
}

5.3、yield 線程讓步

yield方法與sleep不同,yield不會阻塞進程,它只是將運行狀態(tài)的線程轉(zhuǎn)換為就緒狀態(tài)使其被重新調(diào)度一次。java采用的搶占式調(diào)度,不能保證讓步后立即就執(zhí)行其他線程。

static native void yield()

代碼示例:

public class TestThread {
public static void main(String[] args) throws Exception{
Thread t1 =new Thread(()->{
for (int i=0;i<=5;i++){
if (i==2){
Thread.yield(); //調(diào)用yield方法
}
System.out.println("高級輸出: "+i);
}
});

Thread t2 = new Thread(()->{
for (int i=0;i<=5;i++){
System.out.println("低級輸出: "+i);
}
});

t1.setPriority(10);
t2.setPriority(2);
t2.start();
t1.start();
}
}

5.4、join 線程插隊

在線程中調(diào)用其他線程的join方法時,此線程會被阻塞知道join的線程被執(zhí)行完成。

final void join() 
final void join(long millis)

代碼示例:

public class TestThread {
public static void main(String[] args) throws Exception{
Thread t1 =new Thread(()->{
for (int i=0;i<=5;i++){
if (i==2){
Thread.yield();
}
System.out.println("t1: "+i);
}
});
t1.setPriority(2);
t1.start();
for (int i=0;i<=5;i++){
if (i==2){
t1.join();
}
System.out.println("主線程輸出 : "+i);
}
}
}

六、多線程同步

線程安全問題:當多個線程同時去訪問同一個資源時會導致一些安全問題,比如線程A訪問資源c時,資源c的值為1,之后線程A休眠了500毫秒,在此休眠期間線程B去訪問了資源c,導致資源c的值變成0,這是休眠結(jié)束的線程A再去執(zhí)行時資源c的值已經(jīng)變化。為了解決這樣的問題就出現(xiàn)了多線程同步。

多線程同步:限制某個資源在同一時刻只能被一個線程訪問。

6.1、synchronized 同步代碼塊

同步代碼塊是多線程同步的手段之一,當多個線程使用同一個資源時,將處理資源的代碼放置在一個用synchronized關鍵字修飾的代碼塊中,這段代碼塊叫做同步代碼塊。

synchronized(lock){  //lock : 是一個鎖對象,默認為1
//操作資源的代碼
}

原理:同步代碼塊的關鍵在于lock,lock是一個鎖對象,只有l(wèi)ock的標志位為1時線程才能執(zhí)行同步代碼塊。當線程執(zhí)行到同步代碼塊時先檢查lock標志位,默認為1,線程會執(zhí)行同步代碼塊同時lock的值置為0,這時其他線程就發(fā)生阻塞不能執(zhí)行同步代碼塊中的代碼。等線程執(zhí)行完同步代碼塊之后lock又被置為1,以此循環(huán)往復。

重點:鎖對象的類型可以時任意類型,但是各個線程使用的鎖對象必須是同一個。也就是創(chuàng)建鎖對象的代碼不可以寫在run方法,否則每個線程運行都會創(chuàng)建一個自己的鎖對象,形同虛設。

代碼示例:

/******* 使用線程同步 *******/
class MyThread implements Runnable{
private int i=5;
Object lock = new Object();
@Override
public void run() {
while (this.i>0){
synchronized (lock){
if (this.i>0){
try{
Thread.sleep(500);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"在賣第"+this.i+"張票");
}
this.i--;
}
}
}
}
public class ThreadTest {
public static void main(String[] args) {
MyThread myt = new MyThread();
Thread t1 = new Thread(myt,"窗口1");
Thread t2 = new Thread(myt,"窗口2");
t1.start();
t2.start();
}
}
/******* 不使用線程同步 *******/
class MyThread2 implements Runnable{
private int i=5;
Object lock = new Object();
@Override
public void run() {
while (this.i>0){
if (this.i>0){
try{
Thread.sleep(500);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"在賣第"+this.i--+"張票");
}
}
}
}

public class ThreadTest2 {
public static void main(String[] args) {
MyThread2 myt2 = new MyThread2();
Thread t1 = new Thread(myt2,"窗口1");
Thread t2 = new Thread(myt2,"窗口2");
t1.start();
t2.start();
}
}

小結(jié):

對比上面兩段代碼的輸出結(jié)果,在使用synchronized 時,多運行幾次就可以看到輸出結(jié)果可能會有0,或者同一張票在兩個窗口都被賣了一次。相較于使用synchronized 時,可以發(fā)現(xiàn)輸出都是同一個窗口在賣,因為在一個線程進入同步代碼塊之后另一個線程就阻塞了,即使當前線程使用sleep休眠但是lock標志位任然還在0所以另一個線程無法進入執(zhí)行。

6.2、synchronized 同步方法

synchronized 不僅可以修飾代碼塊,也可以修飾方法。

使用:定義同步方法,然后再創(chuàng)建線程對象的run方法中直接調(diào)用同步方法就行。

[修飾符] synchronized [返回值類型] 方法名(){

}

原理:再使用同步代碼塊時需要定義鎖對象,而使用同步方法就沒有這樣的問題。同步方法和同步代碼塊的原理一樣,只不過同步方法的鎖對象就是調(diào)用該方法的對象,就是this指向的對象。同步方法被所有線程共享,同步方法所在的對象相對于所有線程而言是唯一的,當一個線程進入同步方法時其他線程也不能進入同步方法執(zhí)行了。

synchronized 總結(jié):

synchronized 使用一種封閉式鎖機制,優(yōu)點是使用起來非常簡單,同時也有一些缺點,例如無法中斷正在等候獲得鎖的線程,無法通過輪詢獲得鎖等等。

6.3、Lock 同步鎖

JDK5開始增加了Lock 鎖,Lock 鎖功能與synchronized 基本相同,但是Lock鎖可以讓線程再持續(xù)獲得鎖失敗之后不再繼續(xù)等待,使用上也比synchronized 更靈活。

使用:

  1. 導入 import java.util.concurrent.locks.*;
  2. 使用Lock的實現(xiàn)類ReentrantLock創(chuàng)建一個鎖對象
  3. 使用lock方法和unlock方法進行上鎖和解鎖

代碼示例:

import java.util.concurrent.locks.*;

class MyThread implements Runnable{
private int i=5;
private final Lock lk = new ReentrantLock(); //使用Lock的實現(xiàn)類ReentrantLock創(chuàng)建一個鎖對象
@Override
public void run() {
while (this.i>0){
lk.lock(); //上鎖:此時只有當前線程可以使用了
if (this.i>0){
try{
Thread.sleep(500);
lk.unlock(); //解鎖:之后其他線程也可以訪問
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"在賣第"+this.i--+"張票");
}
}
}
}
public class ThreadTest {
public static void main(String[] args) {
MyThread myt = new MyThread();
Thread t1 = new Thread(myt,"窗口1");
Thread t2 = new Thread(myt,"窗口2");
t1.start();
t2.start();

}
}
/*輸出
窗口1在賣第5張票
窗口2在賣第4張票
窗口1在賣第3張票
窗口2在賣第2張票
窗口1在賣第1張票
*/

關于死鎖問題:

兩個正在運行的線程都在等待對方的鎖,從而造成程序的停滯現(xiàn)象稱為死鎖。兩個線程都需要對方占用的鎖,但是二者又無法釋放自己擁有的鎖,于是雙方都處于掛起狀態(tài)。

七、多線程通信

為了控制多個線程按照一定的順序輪流執(zhí)行,就需要讓線程之間進行通信保證線程任務協(xié)調(diào)進行。

線程通信常用方法:

這些方法位于Object類中可以直接使用。

final void wait() // 使當前線程放棄同步鎖進入等待,直到其他線程進入此同步鎖并且調(diào)用notify、notifyAll喚醒為止
final void notify() // 喚醒此同步鎖上等待的第一個調(diào)用wait方法的線程
final void notifyAll() // 喚醒此同步鎖上等待的所有調(diào)用wait方法的線程

代碼示例:

// 廠家線程生產(chǎn)商品commodity數(shù)組加一個元素,商家線程賣出商品commodity刪除一個元素。
import java 網(wǎng)頁名稱:Java多線程小記,你學會了嗎?
轉(zhuǎn)載來于:http://www.dlmjj.cn/article/dpoejog.html