上兩個章節: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修飾的不是整個方法而是某一段代碼塊,因此肯定效率要更有,雖然在在本例中沒有體現。