基本執行緒同步(三)在同步的類裡安排獨立屬性
宣告:本文是《 Java 7 Concurrency Cookbook 》的第二章,作者: Javier Fernández González 譯者:許巧輝 校對:方騰飛
在同步的類裡安排獨立屬性
當你使用synchronized關鍵字來保護程式碼塊時,你必須通過一個物件的引用作為引數。通常,你將會使用this關鍵字來引用執行該方法的物件,但是你也可以使用其他物件引用。通常情況下,這些物件被建立只有這個目的。比如,你在一個類中有被多個執行緒共享的兩個獨立屬性。你必須同步訪問每個變數,如果有一個執行緒訪問一個屬性和另一個執行緒在同一時刻訪問另一個屬性,這是沒有問題的。
在這個指南中,你將學習如何解決這種情況的一個例子,程式設計模擬一家電影院有兩個螢幕和兩個售票處。當一個售票處出售門票,它們用於兩個電影院的其中一個,但不能用於兩個,所以在每個電影院的免費席位的數量是獨立的屬性。
準備工作
這個指南的例子使用Eclipse IDE實現。如果你使用Eclipse或其他IDE,如NetBeans,開啟它並建立一個新的Java專案。
如何做…
按以下步驟來實現的這個例子:
1.建立一個Cinema類,新增兩個long型別的屬性,命名為vacanciesCinema1和vacanciesCinema2。
public class Cinema { private long vacanciesCinema1; private long vacanciesCinema2;
2.給Cinema類新增兩個額外的Object屬性,命名為controlCinema1和controlCinema2。
private final Object controlCinema1, controlCinema2;
3.實現Cinema類的構造方法,初始化所有屬性。
public Cinema(){ controlCinema1=new Object(); controlCinema2=new Object(); vacanciesCinema1=20; vacanciesCinema2=20; }
4.實現sellTickets1()方法,當第一個電影院出售一些門票將呼叫它。使用controlCinema1物件來控制訪問synchronized的程式碼塊。
public boolean sellTickets1 (int number) { synchronized (controlCinema1) { if (number<vacanciesCinema1) { vacanciesCinema1-=number; return true; } else { return false; } } }
5.實現sellTickets2()方法,當第二個電影院出售一些門票將呼叫它。使用controlCinema2物件來控制訪問synchronized的程式碼塊。
public boolean sellTickets2 (int number) { synchronized (controlCinema2) { if (number<vacanciesCinema2) { vacanciesCinema2-=number; return true; } else { return false; } } }
6.實現returnTickets1()方法,當第一個電影院被退回一些票時將呼叫它。使用controlCinema1物件來控制訪問synchronized的程式碼塊。
public boolean returnTickets1 (int number) { synchronized (controlCinema1) { vacanciesCinema1+=number; return true; } }
7.實現returnTickets2()方法,當第二個電影院被退回一些票時將呼叫它。使用controlCinema2物件來控制訪問synchronized的程式碼塊。
public boolean returnTickets2 (int number) { synchronized (controlCinema2) { vacanciesCinema2+=number; return true; } }
8.實現其他兩個方法,用來返回每個電影院空缺位置的數量。
public long getVacanciesCinema1() { return vacanciesCinema1; } public long getVacanciesCinema2() { return vacanciesCinema2; }
9.實現TicketOffice1類,並指定它實現Runnable介面。
public class TicketOffice1 implements Runnable {
10.宣告一個Cinema物件,並實現該類(類TicketOffice1)的構造器用來初始化這個物件。
private Cinema cinema; public TicketOffice1 (Cinema cinema) { this.cinema=cinema; }
11.實現run()方法,用來模擬在兩個電影院的一些操作。
@Override public void run() { cinema.sellTickets1(3); cinema.sellTickets1(2); cinema.sellTickets2(2); cinema.returnTickets1(3); cinema.sellTickets1(5); cinema.sellTickets2(2); cinema.sellTickets2(2); cinema.sellTickets2(2); }
12.實現TicketOffice2類,並指定它實現Runnable介面。
public class TicketOffice2 implements Runnable {
13.宣告一個Cinema物件,並實現該類(類TicketOffice2)的構造器用來初始化這個物件。
private Cinema cinema; public TicketOffice2 (Cinema cinema) { this.cinema=cinema; }
14.實現run()方法,用來模擬在兩個電影院的一些操作。
@Override public void run() { cinema.sellTickets2(2); cinema.sellTickets2(4); cinema.sellTickets1(2); cinema.sellTickets1(1); cinema.returnTickets2(2); cinema.sellTickets1(3); cinema.sellTickets2(2); cinema.sellTickets1(2); }
15.通過建立類名為Main,且包括main()方法來實現這個示例的主類。
public class Main { public static void main(String[] args) {
16.宣告和建立一個Cinema物件。
Cinema cinema=new Cinema();
17.建立一個TicketOffice1物件,並且用執行緒來執行它。
TicketOffice1 ticketOffice1=new TicketOffice1(cinema); Thread thread1=new Thread(ticketOffice1,"TicketOffice1");
18.建立一個TicketOffice2物件,並且用執行緒來執行它。
TicketOffice2 ticketOffice2=new TicketOffice2(cinema); Thread thread2=new Thread(ticketOffice2,"TicketOffice2");
19.啟動這兩個執行緒。
thread1.start(); thread2.start();
20.等待執行緒執行完成。
try { thread1.join(); thread2.join(); } catch (InterruptedException e) { e.printStackTrace(); }
21.兩個電影院的空缺數寫入控制檯。
System.out.printf("Room 1 Vacancies: %d\n",cinema.getVacanciesCinema1()); System.out.printf("Room 2 Vacancies: %d\n",cinema.getVacanciesCinema2());
它是如何工作的…
當你使用synchronized關鍵字來保護程式碼塊,你使用一個物件作為引數。JVM可以保證只有一個執行緒可以訪問那個物件保護所有的程式碼塊(請注意,我們總是談論的物件,而不是類)。
註釋:在這個示例中,我們用一個物件來控制vacanciesCinema1屬性的訪問。所以,在任意時刻,只有一個執行緒能修改該屬性。用另一個物件來控制 vacanciesCinema2屬性的訪問。所以,在任意時刻,只有一個執行緒能修改這個屬性。但是可能有兩個執行緒同時執行,一個修改 vacancesCinema1屬性而另一個修改vacanciesCinema2屬性。
當你執行這個示例,你可以看到每個電影院的空缺數量的最後的結果總是預期的。在以下截圖中,你可以看到應用程式的執行結果:
不止這些…
synchronize關鍵字還有其他重要用法,請見其他指南中解釋這個關鍵字使用的參見部分。
參見
在第2章,基本執行緒同步中在同步程式碼中使用條件的指南。