Java多線程

進程:正在進行中的程序。其實進程就是一個應用程序運行時的內存分配空間。進程負責的是應用程序的空間的標示。java

線程:其實就是進程中一個程序執行控制單元,一條執行路徑。線程負責的是應用程序的執行順序。小程序

  • 一個進程至少有一個線程在運行,當一個進程中出現多個線程時,就稱這個應用程序是多線程應用程序。
  • 每一個線程在棧區中都有本身的執行空間,本身的方法區、本身的變量。

jvm在啓動的時,首先有一個主線程,負責程序的執行,調用的是main函數,主線程執行的代碼都在main方法中。當產生垃圾時,收垃圾的動做,是不須要主線程來完成,由於這樣主線程中的代碼執行會中止,而去運行垃圾回收器代碼,效率較低,因此由單獨一個線程來負責垃圾回收。 安全

隨機性的原理:哪一個線程獲取到了cpu的執行權,哪一個線程就執行,實質是cpu的快速切換形成。多線程

返回當前線程的名稱:Thread.currentThread().getName();線程的名稱是由:Thread-編號定義的。編號從0開始。線程要運行的代碼都統一存放在了run方法中。併發

線程要運行必需要經過類中指定的方法【start方法】開啓。(啓動後,就多了一條執行路徑)jvm

start方法ide

  1. 啓動了線程
  2. 讓jvm調用了run方法。

建立線程的第一種方式:繼承Thread ,由子類複寫run方法:函數

步驟:this

  1. 定義類繼承Thread類;
  2. 目的是複寫run方法,將要讓線程運行的代碼都存儲到run方法中;
  3. 經過建立Thread類的子類對象,建立線程對象;
  4. 調用線程的start方法,開啓線程,並執行run方法。
    class Show extends Thread   
    {  
        public void run()  
        {  
            for (int i =0;i<5 ;i++ )  
            {  
                System.out.println(name +"_" + i);  
            }  
        }  
      
        public Show(){}  
        public Show(String name)  
        {  
            this.name = name;  
        }  
        private String name;  
      
        public static void main(String[] args)   
        {  
            new Show("csdn").start();  
            new Show("黑馬").start();  
        }  
    }  

【可能的運行結果】:spa

爲何咱們不能直接調用run()方法呢?緣由是線程的運行須要本地操做系統的支持。

查看start的源代碼發現:

    public synchronized void start() {  
         
           if (threadStatus != 0)  
               throw new IllegalThreadStateException();  
      
           group.add(this);  
      
           boolean started = false;  
           try {  
               start0();  
               started = true;  
           } finally {  
               try {  
                   if (!started) {  
                       group.threadStartFailed(this);  
                   }  
               } catch (Throwable ignore) {  
                   /* do nothing. If start0 threw a Throwable then 
                     it will be passed up the call stack */  
               }  
           }  
       }  
      
    private native void start0();  

這個方法用了native關鍵字,native表示調用本地操做系統的函數,多線程的實現須要本地操做系統的支持。

線程狀態

  • 被建立:start()
  • 運行:具有執行資格,同時具有執行權;
  • 凍結:sleep(time),wait()—notify()喚醒;線程釋放了執行權,同時釋放執行資格;
  • 臨時阻塞狀態:線程具有cpu的執行資格,沒有cpu的執行權;
  • 消亡:stop()

建立線程的第二種方式:實現一個接口Runnable:

步驟:

  1. 定義類實現Runnable接口。
  2. 覆蓋接口中的run方法(用於封裝線程要運行的代碼)。
  3. 經過Thread類建立線程對象;
  4. 將實現了Runnable接口的子類對象做爲實際參數傳遞給Thread類中的構造函數。【爲何要傳遞呢?由於要讓線程對象明確要運行的run方法所屬的對象】
  5. 調用Thread對象的start方法,開啓線程,並運行Runnable接口子類中的run方法。

Ticket t = new Ticket();

直接建立Ticket對象,並非建立線程對象。【由於建立線程對象只能經過new Thread類,或者new Thread類的子類才能夠

Thread t1 = new Thread(t); //建立線程。

只要將t做爲Thread類的構造函數的實際參數傳入便可完成線程對象和t之間的關聯。【爲何要將t傳給Thread類的構造函數呢?其實就是爲了明確線程要運行的代碼run方法】

t1.start();//開啓線程

爲何要有Runnable接口的出現?

1:經過繼承Thread類的方式,能夠完成多線程的創建。可是這種方式有一個侷限性,若是一個類已經有了本身的父類,就不能夠繼承Thread類,由於java單繼承的侷限性。

但是該類中的還有部分代碼須要被多個線程同時執行,這時怎麼辦呢?

只有對該類進行額外的功能擴展,java就提供了一個接口Runnable。這個接口中定義了run方法,其實run方法的定義就是爲了存儲多線程要運行的代碼。

因此,一般建立線程都用第二種方式。【由於實現Runnable接口能夠避免單繼承的侷限性】

2:實際上是將不一樣類中須要被多線程執行的代碼進行抽取。將多線程要運行的代碼的位置單獨定義到接口中。爲其餘類進行功能擴展提供了前提。

因此Thread類在描述線程時,內部定義的run方法,也來自於Runnable接口。

實現Runnable接口能夠避免單繼承的侷限性。並且,繼承Thread,是能夠對Thread類中的方法,進行子類複寫的。可是不須要作這個複寫動做的話,只爲定義線程代碼存放位置,實現Runnable接口更方便一些。因此Runnable接口將線程要執行的任務封裝成了對象。

new Thread(new Runnable(){  //匿名
public void run()
{
    System.out.println("runnable run");
}
})

{
public void run()
{
    System.out.println("subthread run");
}

}.start();  //結果:subthread run
Try {
Thread.sleep(10);
}catch(InterruptedException e){}// 當刻意讓線程稍微停一下,模擬cpu 切換狀況。

Thread和Runnable的區別:

若是一個類繼承Thread,則不能資源共享(有多是操做的實體不是惟一的);可是若是實現了Runable接口的話,則能夠實現資源共享。

    class Show implements Runnable  
    {  
        private int count = 10;//假設有10張票  
        @Override  
        public void run()  
        {  
            for (int i = 0; i < 5 ; i++ )  
            {  
                if (this.count > 0)  
                {  
                    System.out.println(Thread.currentThread().getName()+"正在賣票" + this.count--);  
                }  
            }  
        }  
      
        public static void main(String[] args)   
        {  
            Show s = new Show(); //注意必須保證只對1個實體s操做  
            new Thread(s,"窗口1").start();  
            new Thread(s,"窗口2").start();  
            new Thread(s,"窗口3").start();  
        }  
    }  

實現Runnable接口比繼承Thread類所具備的優點:

  • 適合多個相同的程序代碼的線程去處理同一個資源
  • 能夠避免java中的單繼承的限制
  • 增長程序的健壯性,代碼能夠被多個線程共享,代碼和數據獨立。

設置線程優先級

Thread t = new Thread(myRunnable);  
t.setPriority(Thread.MAX_PRIORITY);//一共10個等級,Thread.MAX_PRIORITY表示最高級10  
t.start();

//MAX_PRIORITY : 其值是 10 
//MIN_PRIORITY : 其值是 1
//NORM_PRIORITY: 其值是 5

提示:主線程的優先級是5,不要誤覺得優先級越高就先執行。誰先執行仍是取決於誰先去的CPU的資源

控制線程的方法:

  • join方法:假如你在A線程中調用了B線程的join方法B.join();,這時B線程繼續運行,A線程中止(進入阻塞狀態)。等B運行完畢A再繼續運行。
  • sleep方法:線程中調用sleep方法後,本線程中止(進入阻塞狀態),運行權交給其餘線程。
  • yield方法:線程中調用yield方法後本線程並不中止,運行權由本線程和優先級不低於本線程的線程來搶。(不必定優先級高的能先搶到,只是優先級高的搶到的時間長)
package heimablog;

// 定義Runnable 接口的實現類
public class JoinTest implements Runnable {
	// 重寫run() 方法
	public void run() {
		for (int i = 0; i <= 10; ++i)
			System.out.println(Thread.currentThread().getName() + "..." + i);
	}

	public static void main(String args[]) throws InterruptedException {
		// 建立Runnable 接口實現類的實例
		JoinTest t = new JoinTest();
		// 經過 Thread(Runnable target) 建立新線程
		Thread thread = new Thread(t);
		Thread thread1 = new Thread(t);
		// 啓動線程
		thread.start();
		// 只有等thread 線程執行結束,main 線程纔會向下執行;
		thread.join();
		System.out.println("thread1 線程將要啓動");
		thread1.start();
	}
} 
  • wait方法:當前線程轉入阻塞狀態,讓出cpu的控制權,解除鎖定。
  • notify方法:喚醒由於wait()進入阻塞狀態的其中一個線程。
  • notifyAll方法: 喚醒由於wait()進入阻塞狀態的全部線程。

這三個方法都必須用synchronized塊來包裝,並且必須是同一把鎖,否則會拋出java.lang.IllegalMonitorStateException異常。

多線程安全問題的緣由:

發現一個線程在執行多條語句時,並運算同一個數據時,在執行過程當中,其餘線程參與進來,並操做了這個數據,致使了錯誤數據的產生。

涉及到兩個因素:

  1. 多個線程在操做共享數據。
  2. 有多條語句對共享數據進行運算。

以下面程序:

package heimablog;

/* 
 * 多個線程同時訪問一個數據時,出現的安全問題。
 * 模擬一個賣火車票系統:一共有100張票,多個窗口同時賣票
 */
class Ticks implements Runnable 
{
    private int ticks = 100 ; 
    public void run()
    {
        while (ticks > 0)
        {
        	//加入sleep 方法是爲了更明顯的看到該程序中出現的安全問題。
            try{Thread.sleep(10);}catch (Exception e) {} 
            System.out.println(Thread.currentThread().getName()
                    +"...賣出了第"+ticks+"張票");
            ticks -- ;
        }
    }
}
public class ShowTest {
    public static void main(String args[])
    {
    	//建立 Runnable 實現類 Ticks 的對象。
        Ticks t = new Ticks() ;  
        //開啓4個線程處理同一個 t 對象。
        new Thread(t , "一號窗口").start() ; 
        new Thread(t , "二號窗口").start() ; 
        new Thread(t , "三號窗口").start() ; 
        new Thread(t , "四號窗口").start() ; 
    }
}

運行的結果會出現"一號窗口...賣出了第0張票,四號窗口...賣出了第-1張票,二號窗口...賣出了第-2張票"這樣的安全問題。

解決安全問題的原理

只要將操做共享數據的語句在某一時段讓一個線程執行完,在執行過程當中,其餘線程不能進來執行就能夠解決這個問題。

解決這類問題的方法:

  一、同步代碼塊。

  二、同步函數。

同步代碼塊

synchronized(obj)  // 任意對象均可以,這個對象就是鎖。
{
    //此處的代碼就是同步代碼塊,也就是須要被同步的代碼;
}

synchronized 後括號裏面的 obj 就是同步監視器。代碼含義:線程開始執行同步代碼塊以前,必須先得到對同步監視器的鎖定。即只有得到對同步監視器的鎖定的線程能夠在同步中執行,沒有鎖定的線程即便得到執行權,也不能在同步代碼塊中執行。

注意:雖然JAVA 程序容許使用任何對象來做爲同步監視器。可是仍是推薦使用可能被併發訪問的共享資源來充當同步監視器。

修改代碼以下:

package heimablog;

/* 
 * 多個線程同時訪問一個數據時,出現的安全問題。
 * 模擬一個賣火車票系統:一共有100張票,多個窗口同時賣票
 */
class Ticks implements Runnable {
	private int ticks = 100;

	public void run() {
		while (ticks > 0) {
			synchronized (Ticks.class) {
				if (ticks > 0) {
					try {
						Thread.sleep(10);
					} catch (Exception e) {}
					System.out.println(Thread.currentThread().getName()
							+ "...賣出了第" + ticks + "張票");
					ticks--;
				}
			}
		}
	}
}

public class ShowTest {
	public static void main(String args[]) {
		// 建立 Runnable 實現類 Ticks 的對象。
		Ticks t = new Ticks();
		// 開啓4個線程處理同一個 t 對象。
		new Thread(t, "一號窗口").start();
		new Thread(t, "二號窗口").start();
		new Thread(t, "三號窗口").start();
		new Thread(t, "四號窗口").start();
	}
}

加入同步監視器以後的程序就不會出現數據上的錯誤了。

  雖然同步監視器的好處是解決了多線程的安全問題。但也也由於多個線程須要判斷鎖,較爲消耗資源。

同步前提:

  1. 必需要有兩個或者兩個以上的線程,才須要同步。
  2. 多個線程必須保證使用的是同一個鎖。

若是加入了synchronized 同步監視器,還出現了安全問題,則能夠按照以下步驟找尋錯誤:

  1. 明確那些代碼是多線程代碼。
  2. 明確共享數據。
  3. 明確多線程運行代碼中那些代碼是操做共享數據的。

同步函數

  把 synchronized 做爲修飾符修飾函數。則該函數稱爲同步函數。

  注意:同步函數無需顯示的指定同步監視器,函數都有本身所屬的對象this,同步函數的同步監視器是this,也就是該對象自己。

  注意:synchronized 關鍵字能夠修飾方法,能夠修飾代碼塊,但不能修飾構造器、屬性等。

上面經過模擬火車賣票系統的小程序,經過加入 synchronized 同步監視器,來解決多線程中的安全問題。下面模擬銀行取錢問題,經過同步函數來解決多線程的安全問題。

package heimablog;

class Account {
	// 帳戶餘額
	private double balance;

	public Account(double balance) {
		this.balance = balance;
	}

	// get和set方法
	public double getBalance() {
		return balance;
	}

	public void setBalance(double balance) {
		this.balance = balance;
	}

	// 同步函數
	// 提供一個線程安全的draw的方法來完成取錢操做。
	public synchronized void Draw(double drawAmount) {
		if (balance >= drawAmount) {
			System.out.println(Thread.currentThread().getName() + "取錢成功!吐出金額"
					+ drawAmount);

			try {
				Thread.sleep(10);
			} catch (Exception e) {
			}
			balance -= drawAmount;
			System.out.println("卡上餘額:" + balance);
		} else {
			System.out.println(Thread.currentThread().getName() + "取錢失敗!卡上餘額:"
					+ balance);
		}
	}
}

class DrawThread implements Runnable {
	// 模擬帳戶
	private Account account;
	// 但願所取錢的金額
	private double drawAmount;

	private boolean flag = true;

	public DrawThread(Account account, double drawAmount) {
		this.account = account;
		this.drawAmount = drawAmount;
	}

	// 當前取錢
	public void run() {
		try {
			while (flag) {
				// 調用取錢函數
				account.Draw(drawAmount);
			}
		} catch (Exception e) {
			flag = false;
		}
	}
}

public class ShowTest {
	public static void main(String args[]) {
		Account account = new Account(10000);
		DrawThread draw = new DrawThread(account, 50);
		Thread t = new Thread(draw, "A.....");
		Thread t1 = new Thread(draw, "B.");
		t.start();
		t1.start();
	}
}

同步方法的監視器是 this ,所以對於同一個 Account 而言,任意時刻只能有一條線程得到 Account 對象的鎖定。

提示:可變類的線程安全是以下降運行程序的運行效率做爲代價,爲了減小線程安全所帶來的負面影響,程序能夠採用以下策略:

  • 只對會改變競爭資源的方法進行同步。
  • 在兩個或兩個以上的線程操做同一個鎖的環境中使用同步。

當以下狀況發生時會釋放同步監視器的鎖定:

  • 當前線程的同步方法、同步代碼塊執行結束。
  • 當前線程的同步方法、同步代碼塊中遇到break 、 return終止了該代碼塊、該方法的繼續執行。
  • 當前線程的同步方法、同步代碼塊出現了未處理的Error或Exception,致使該代碼塊、該方法異常結束時會釋放同步鎖。
  • 當線程執行同步方法、同步代碼塊,程序執行了同步監視器對象的wait() 方法時。

當同步函數被static修飾時,這時的同步用的是哪一個鎖呢?

靜態函數在加載時所屬於類,這時有可能尚未該類產生的對象,可是該類的字節碼文件加載進內存就已經被封裝成了對象,這個對象就是該類的字節碼文件對象。

因此靜態加載時,只有一個對象存在,那麼靜態同步函數就使用的這個對象。這個對象就是 類名.class

同步代碼塊和同步函數的區別?

  • 同步代碼塊使用的鎖能夠是任意對象。
  • 同步函數使用的鎖是this,靜態同步函數的鎖是該類的字節碼文件對象。

在一個類中只有一個同步,可使用同步函數。若是有多同步,必須使用同步代碼塊,來肯定不一樣的鎖。因此同步代碼塊相對靈活一些

死鎖

  當兩線程相互等待對方釋放鎖時,就會發生死鎖。因爲JVM沒有監測,也沒有采用措施來處理死鎖,因此多線程編成時應該採起措施來避免死鎖。

單例模式之懶漢式

懶漢式:延遲加載方式。

當多線程訪問懶漢式時,由於懶漢式的方法內對共性數據進行多條語句的操做,因此容易出現線程安全問題。

  • 爲了解決安全問題,加入同步機制。可是卻帶來了效率下降。
  • 爲了解決效率問題,經過雙重判斷的形式解決。
class Single {

	private static Single s = null;
     private Single() {
	
     } public static Single getInstance() { if (s == null) { synchronized (Single.class) {//用字節碼文件對象做爲鎖; if (s == null) s = new Single(); } } return s; } }

同步死鎖:一般只要將同步進行嵌套,就能夠看到現象。

線程通訊

思路:多個線程在操做同一個資源,可是操做的動做卻不同。

  1. 將資源封裝成對象。
  2. 將線程執行的任務(任務其實就是run方法。)也封裝成對象。

模擬生產消費者:系統在有兩條線程,分別表明生成者和消費者。

  程序的基本流程:

  1. 生成者生產出一件商品。
  2. 消費者消費生成出的商品。

  經過上訴流程要了解:生成者和消費者不能連續生成或消費商品,同時消費者只能消費已經生產出的商品。

package heimablog;

class Phone {
	// 定義商品的編號
	private int No;
	// 定義商品的名字
	private String name;
	private boolean flag = true;

	// 初始化商品的名字和編號,同時編號是自增的
	public Phone(String name, int No) {
		this.name = name;
		this.No = No;
	}

	// 定義商品中的消費方法和生產方法。用synchronized 修飾符修飾
	public synchronized void Production() {

		// 致使當前線程等待,知道其餘線程調用notify()或notifyAll()方法來喚醒
		// if (!flag)
		while (!flag)
			try {
				this.wait();
			} catch (Exception e) {
			}
		System.out.println(Thread.currentThread().getName() + ":生產" + name
				+ ";編號爲:" + ++No);
		// 喚醒在此同步監視器上等待的單個線程。
		// this.notify() ;
		// 喚醒在此同步監視器上等待的全部線程。
		this.notifyAll();
		flag = false;
	}

	public synchronized void Consumption() {
		// if(flag)
		while (flag)
			try {this.wait();} catch (Exception e) {}
		System.out.println(Thread.currentThread().getName() + ";消費商品:" + name
				+ "商品的編號爲" + No);
		// this.notify() ;
		this.notifyAll();
		flag = true;
	}
}

class ProducerThread implements Runnable {
	Phone phone;
	private boolean flag = true;

	// 同步監視器的對象
	public ProducerThread(Phone phone) {
		this.phone = phone;
	}

	public void run() {
		try {
			while (flag)
				phone.Production();
		} catch (Exception e) {
			flag = false;
		}
	}
}

class ConsumptionThread implements Runnable {
	Phone phone;
	private boolean flag = true;

	// 同步監視器的對象
	public ConsumptionThread(Phone phone) {
		this.phone = phone;
	}

	public void run() {
		try {
			while (flag)
				phone.Consumption();
		} catch (Exception e) {
			flag = false;
		}
	}
}

public class ShowTest {
	public static void main(String args[]) {
		Phone phone = new Phone("iPhone 5", 0);
		new Thread(new ProducerThread(phone), "生成者000").start();
		new Thread(new ProducerThread(phone), "生成者111").start();
		new Thread(new ConsumptionThread(phone), "消費者000").start();
		new Thread(new ConsumptionThread(phone), "消費者111").start();
	}
}

上面的程序中:flag 標誌位 是判斷 是由生產者生成仍是由消費者進行消費。其實,在現實生活中,不可能只有一個生成者和消費者,而是多個生成者和消費者。因此用 while 循環來進行 flag 的判斷 , 而不是用 if 。若是用if 容易出現線程安全問題;並且在用while 循環進行flag的判斷時,則必須用 notifyAll() 方法來喚醒同步監視器中全部等待中的線程,而不是 用notify() 方法。用notify()  則會致使全部線程進入等待狀態。

上面的小程序藉助Object 類提供的 wait()、notify()、notifyAll 三個方法【等待喚醒機制涉及的方法】

  • wait() :致使當前線程等待,知道其餘線程調用該同步監視器的notify()或notifyAll() 方法來喚醒線程。
  • notify() : 喚醒在此同步監視器上等待的單個線程。若是全部線程都在此同步監視器上等待,則會選擇一個其中一個喚醒。
  • notifyAll() :喚醒此同步監視器上等待的全部單個線程。

注意

  • 這三個方法必須用同步監視器對象來調用:
    1. 同步函數:由於該類的默認實例(this)就是同步監視器,因此能夠在同步方法中直接調用。
    2. 同步代碼塊:必須使用 synchronized 括號中的對象來調用。
  • 這些方法都須要定義在同步中:由於這些方法必需要標示所屬的鎖。你要知道 A鎖上的線程被wait了,那這個線程就至關於處於A鎖的線程池中,只能A鎖的notify喚醒。
  • 這三個方法都定義在Object類中。爲何操做線程的方法定義在Object類中?由於這三個方法都須要定義同步內,並標示所屬的同步鎖,既然被鎖調用,而鎖又能夠是任意對象,那麼能被任意對象調用的方法必定定義在Object類中。

wait和sleep區別

分析這兩個方法從執行權和鎖上來分析:

wait:能夠指定時間也能夠不指定時間。不指定時間,只能由對應的notify或者notifyAll來喚醒。

sleep:必須指定時間,時間到自動從凍結狀態轉成運行狀態(臨時阻塞狀態)。

wait:線程會釋放執行權,並且線程會釋放鎖。

Sleep:線程會釋放執行權,但不是不釋放鎖。

線程的中止【stop方法已過期】

原理:讓線程運行的代碼結束,也就是結束run方法。

怎麼結束run方法?通常run方法裏確定定義循環。因此只要結束循環便可。

  • 第一種方式:定義循環的結束標記。
  • 第二種方式:若是線程處於了凍結狀態,是不可能讀到標記的,這時就須要經過Thread類中的interrupt方法,將其凍結狀態強制清除。讓線程恢復具有執行資格的狀態,讓線程能夠讀到標記,並結束。

Thread's functions

  • interrupt():中斷線程。
  • setPriority(int newPriority):更改線程的優先級。
  • getPriority():返回線程的優先級。
  • toString():返回該線程的字符串表示形式,包括線程名稱、優先級和線程組。
  • Thread.yield():暫停當前正在執行的線程對象,並執行其餘線程。
  • setDaemon(true):將該線程標記爲守護線程或用戶線程。當正在運行的線程都是守護線程時,Java 虛擬機退出。該方法必須在啓動線程前調用。
  • join:臨時加入一個線程的時候可使用join方法。當A線程執行到了B線程的join方式。A線程處於凍結狀態,釋放了執行權,B開始執行。A何時執行呢?只有當B線程運行結束後,A才從凍結狀態恢復運行狀態執行。

同步鎖LOCK

  JDK 1.5以後,JAVA提供了另一種線程同步機制:顯示定義同步鎖來實現同步,解決線程安全問題使用同步的形式,(同步代碼塊,要麼同步函數)其實最終使用的都是鎖機制。同步鎖應該使用Lock對象充當。在面向對象中誰擁有數據誰就對外提供操做這些數據的方法 ,發現獲取鎖,或者釋放鎖的動做應該是鎖這個事物更清楚。因此將這些動做定義在了鎖當中,並把鎖定義成對象。線程進入同步就是具有了鎖,執行完,離開同步,就是釋放了鎖。

class X {
	// 定義鎖對象
	private final ReentrantLock lock = new ReentrantLock();

	// 定義須要保證線程安全的方法
	public void m() {
		// 加鎖
		lock.lock();
		try {
			// 須要保證線程安全的代碼
		} finally {
			lock.unlock();
		}
	}
}

因此同步是隱示的鎖操做,而Lock對象是顯示的鎖操做,它的出現就替代了同步。

如今鎖是指定對象Lock。因此查找等待喚醒機制方式須要經過Lock接口來完成。而Lock接口中並無直接操做等待喚醒的方法,而是將這些方式又單獨封裝到了一個對象中。這個對象就是Condition,將Object中的三個方法進行單獨的封裝,並提供了功能一致的方法 await()、signal()、signalAll().

Condition接口:await()、signal()、signalAll();

Condition 實例實質上被綁定在一個Lock 對象上。如:

//定義鎖對象
private final ReentrantLock lock = new ReentrantLock() ; 
//指定Lock 對象對應的條件變量
private final Condition condition = lock.newCondition() ; 
  • await() : 相似 wait() 方法。
  • signal() : 相似 notify() 方法。
  • signalAll() : 相似 notifyAll() 方法。
相關文章
相關標籤/搜索