1. 程式人生 > >java----day27(多執行緒)

java----day27(多執行緒)

執行緒安全

如果有多個執行緒在同時執行,而這些執行緒可能會同時執行這段程式碼。程式每次執行結果和單執行緒執行的結果是一樣的,而且其他的變數的值也和預期的是一樣的,就是執行緒安全的。

  • 我們通過一個案例,演示執行緒的安全問題:

電影院要賣票,我們模擬電影院的賣票過程。假設要播放的電影是 “功夫熊貓3”,本次電影的座位共100個(本場電影只能賣100張票)。

我們來模擬電影院的售票視窗,實現多個視窗同時賣 “功夫熊貓3”這場電影票(多個視窗一起賣這100張票)

需要視窗,採用執行緒物件來模擬;需要票,Runnable介面子類來模擬

public class ThreadDemo {
	public static void main(String[] args) {
		//建立票物件
		Ticket ticket = new Ticket();
		
		//建立3個視窗
		Thread t1  = new Thread(ticket, "視窗1");
		Thread t2  = new Thread(ticket, "視窗2");
		Thread t3  = new Thread(ticket, "視窗3");
		
		t1.start();
		t2.start();
		t3.start();
	}
}

模擬票

public class Ticket implements Runnable {
	//共100票
	int ticket = 100;

	@Override
	public void run() {
		//模擬賣票
		while(true){
			if (ticket > 0) {
				//模擬選坐的操作
				try {
					Thread.sleep(1);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName() + "正在賣票:" + ticket--);
			}
		}
	}

其實,執行緒安全問題都是由全域性變數及靜態變數引起的。若每個執行緒中對全域性變數、靜態變數只有讀操作,而無寫操作,一般來說,這個全域性變數是執行緒安全的;若有多個執行緒同時執行寫操作,一般都需要考慮執行緒同步,否則的話就可能影響執行緒安全。

執行緒同步

java中提供了執行緒同步機制,它能夠解決上述的執行緒安全問題。

執行緒同步的方式有兩種:

  • 同步程式碼塊

同步程式碼塊: 在程式碼塊宣告上 加上synchronized

synchronized (鎖物件) {

    可能會產生執行緒安全問題的程式碼

}

同步程式碼塊中的鎖物件可以是任意的物件;但多個執行緒時,要使用同一個鎖物件才能夠保證執行緒安全。

synchronized (lock){
				if (ticket > 0) {
					//模擬電影選坐的操作
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName() + "正在賣票:" + ticket--);
				}
			}

當使用了同步程式碼塊後,上述的執行緒的安全問題,解決了。

  • 同步方法

同步方法:在方法宣告上加上synchronized

public synchronized void method(){

   可能會產生執行緒安全問題的程式碼

}

同步方法中的鎖物件是 this

  • 靜態同步方法: 在方法宣告上加上static synchronized

public static synchronized void method(){

可能會產生執行緒安全問題的程式碼

}

靜態同步方法中的鎖物件是 類名.class

死鎖

同步鎖使用的弊端:當執行緒任務中出現了多個同步(多個鎖)時,如果同步中嵌套了其他的同步。這時容易引發一種現象:程式出現無限等待,這種現象我們稱為死鎖。這種情況能避免就避免掉。

//同步鎖,可能出現死鎖情況

synchronzied(A鎖){

    synchronized(B鎖){

        

     }

}

Lock介面

查閱Lock介面描述,Lock 實現提供了比使用 synchronized 方法和語句可獲得的更廣泛的鎖定操作

Lock介面中的常用方法

Lock提供了一個更加面對物件的鎖,在該鎖中提供了更多的操作鎖的功能。

我們使用Lock介面,以及其中的lock()方法和unlock()方法替代同步,對電影院賣票案例中Ticket類進行如下程式碼修改

public class Ticket implements Runnable {
	//共100票
	int ticket = 100;
	
	//建立Lock鎖物件
	Lock ck = new ReentrantLock();
	
	@Override
	public void run() {
		//模擬賣票
		while(true){
			//synchronized (lock){
			ck.lock();
				if (ticket > 0) {
					//模擬選坐的操作
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName() + "正在賣票:" + ticket--);
				}
			ck.unlock();
			//}
		}
	}
}

等待喚醒機制

在開始講解等待喚醒機制之前,有必要搞清一個概念——執行緒之間的通訊:多個執行緒在處理同一個資源,但是處理的動作(執行緒的任務)卻不相同。通過一定的手段使各個執行緒能有效的利用資源。而這種手段即—— 等待喚醒機制

等待喚醒機制所涉及到的方法:

  1. wait() :等待,將正在執行的執行緒釋放其執行資格 和 執行權,並存儲到執行緒池中。
  2. notify():喚醒,喚醒執行緒池中被wait()的執行緒,一次喚醒一個,而且是任意的。
  3. notifyAll(): 喚醒全部:可以將執行緒池中的所有wait() 執行緒都喚醒。

其實,所謂喚醒的意思就是讓 執行緒池中的執行緒具備執行資格。必須注意的是,這些方法都是在 同步中才有效。同時這些方法在使用時必須標明所屬鎖,這樣才可以明確出這些方法操作的到底是哪個鎖上的執行緒。

仔細檢視JavaAPI之後,發現這些方法 並不定義在 Thread中,也沒定義在Runnable介面中,卻被定義在了Object類中,為什麼這些操作執行緒的方法定義在Object類中?

因為這些方法在使用時,必須要標明所屬的鎖,而鎖又可以是任意物件。能被任意物件呼叫的方法一定定義在Object類中。

如上圖說示,輸入執行緒向Resource中輸入name ,sex , 輸出執行緒從資源中輸出,先要完成的任務是:

1.當input發現Resource中沒有資料時,開始輸入,輸入完成後,叫output來輸出。如果發現有資料,就wait();

2.當output發現Resource中沒有資料時,就wait() ;當發現有資料時,就輸出,然後,叫醒input來輸入資料。

知識點總結

同步鎖

多個執行緒想保證執行緒安全,必須要使用同一個鎖物件

 

  • 同步程式碼塊

         synchronized (鎖物件){

                可能產生執行緒安全問題的程式碼

           }

同步程式碼塊的鎖物件可以是任意的物件

 

  • 同步方法

         public synchronized void method()

              可能產生執行緒安全問題的程式碼

          }

同步方法中的鎖物件是 this

  • 靜態同步方法

       public synchronized void method()

              可能產生執行緒安全問題的程式碼

          }

靜態同步方法中的鎖物件是 類名.class

多執行緒有幾種實現方案,分別是哪幾種

a, 繼承Thread類

b, 實現Runnable介面

c, 通過執行緒池,實現Callable介面

啟動一個執行緒是run()還是start()?它們的區別

啟動一個執行緒是start()

    start: 啟動執行緒,並呼叫執行緒中的run()方法

     run  : 執行該執行緒物件要執行的任務

sleep()和wait()方法的區別

sleep: 不釋放鎖物件, 釋放CPU使用權

          在休眠的時間內,不能喚醒

wait(): 釋放鎖物件, 釋放CPU使用權

           在等待的時間內,能喚醒

為什麼wait(),notify(),notifyAll()等方法都定義在Object類中

鎖物件可以是任意型別的物件