java 併發編程(三)之synchronized


上兩個章節:java 併發編程(一)之synchronized、java 併發編程(二)之synchronized實例

中講解了synchronized關鍵字對同步的控制以及演示了在修飾方法的例子。

我們會發現添加synchronized關鍵字的方法能夠完成多個線程對於同一個方法的同步,但顯然不是很好的實現,因爲首要

問題就是“效率”,很明顯,我們採用高併發設計程序目的就是最大限度的利用cpu的時間片來達到高效的目的,然而在引入

synchronized後,所有線程對於同一方法的訪問將是“串行”的。

因此我們在設計“高併發”的時候,如果要採用synchronized機制進行同步,有一個原則:“在保證同步的前提下,臨界區(critical section)最小化”

synchronized也可以通過輸入“對象”來對多個線程訪問同一“代碼塊”進行同步。通常這個對象是“方法”所在“對象”的引用(this),然而我們也可以

通過傳入“非依賴屬性”對同步代碼塊進行同步。


下面我們給出了一個例子:一個電影院有兩個售票處和兩個放映廳,每個售票處售出的電影票同時只能應用於一個放映廳。因此我們需要對兩個售票處

的售票行爲進行“同步”。

我們編寫了四個類:

Cinema:模擬電影院,vCinema1記錄放映廳1的票數,vCinema2記錄放映廳2的票數

CinemaOffice1:模擬售票處1,售出放映廳1的電影票

CinemaOffice2 :模擬售票處2,售出放映廳2的電影票

Test:測試類


package com.z.cinema_2;

public class Cinema {
	
	private int vCinema1;
	private int vCinema2;
	
	private final Object cinemaController1;
	private final Object cinemaController2;
	
	public Cinema(){
		vCinema1=20;
		vCinema2=20;
		cinemaController1 = new Object();
		cinemaController2 = new Object();
	}
	
	public boolean sellTicket1(int ticketNum){
		
		synchronized(cinemaController1){
			if(vCinema1 <=0 || vCinema1 < ticketNum){
				return false;
			}else{
				vCinema1-=ticketNum;
				return true;
			}
		}
		
	}
	
	public boolean sellTicket2(int ticketNum){
		
		synchronized(cinemaController2){
			if(vCinema2 >0 && vCinema2 >= ticketNum){
				vCinema2-=ticketNum;
				return true;
			}else return false;
		}
		
	}
	
	public void returnTicket1(int ticketNum){
		
		synchronized(cinemaController1){
			vCinema1+=ticketNum;
		}
		
	}
	
	public void returnTicket2(int ticketNum){
		
		synchronized(cinemaController2){
			vCinema2+=ticketNum;
		}
		
	}
	
	public int getTicket1(){
		return vCinema1;
	}
	
	public int getTicket2(){
		return vCinema2;
	}<pre name="code" class="java">package com.z.cinema_2;

public class CinemaOffice2 implements Runnable {
	
	private Cinema cinema;
	
	public CinemaOffice2(Cinema cinema){
		this.cinema = cinema;
	}

	@Override
	public void run() {
		cinema.sellTicket2(2);
		cinema.sellTicket2(4);
		cinema.sellTicket2(6);
		cinema.returnTicket2(1);
		cinema.returnTicket2(3);
		cinema.returnTicket2(5);
	}

}

}


package com.z.cinema_2;

public class CinemaOffice1 implements Runnable{
	
	private Cinema cinema;
	
	public CinemaOffice1(Cinema cinema){
		this.cinema = cinema;
	}
	
	public void run(){
		cinema.sellTicket1(1);
		cinema.sellTicket1(3);
		cinema.sellTicket1(5);
		cinema.returnTicket1(2);
		cinema.returnTicket1(4);
		cinema.returnTicket1(6);
	}

}

package com.z.cinema_2;

public class Test {
	
	public static void main(String[] args){
		Cinema cinema = new Cinema();
		Thread cinemaOffice1Thread = new Thread(new CinemaOffice1(cinema));
		Thread cinemaOffice2Thread = new Thread(new CinemaOffice2(cinema));
		cinemaOffice1Thread.start();
		cinemaOffice2Thread.start();
		try {
			cinemaOffice1Thread.join();
			cinemaOffice2Thread.join();
		} catch (InterruptedException e) {
			System.err.println(e);
		}
		System.out.println(""
				+ "Cinema2 餘票:"
				+ cinema.getTicket1()
				+ "\r"
				+ "Cinema1 餘票:"
				+ cinema.getTicket2());
		
	}

}

運行結果:

Cinema1 餘票:23
Cinema2 餘票:17


我們可以看出,運行結果是我們預期的。

在Cinema中,我們通過兩個“非依賴屬性“同步了兩類操作,一類是對Ticket1的發放和退票操作,一類是對Ticket2的發放和退票操作。

通過這兩個”非依賴屬性“完成對這兩種操作的同步。

同時我們比較之前的例子,因爲此時synchronized修飾的不是整個方法而是某一段代碼塊,因此肯定效率要更有,雖然在在本例中沒有體現。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章