Có một siêu thị gồm có ba cửa phía tây, cửa phía đông và cửa phía nam, khách hàng có thể vào siêu thị thông qua một trong ba cửa này, sơ đồ siêu thị được biểu diễn như hình 3.1. Người quản lý muốn biết có bao nhiêu người trong siêu thị tại một thời điểm bất kỳ nào đó.
Hình 3.1. Sơ đồ siêu thị.
Chúng ta chỉ xét đến trường hợp đơn giản là mọi người chỉ được phép vào mà không được phép ra khỏi siêu thị. Chương trình tương tranh để thực hiện tính số người theo yêu cầu bao gồm ba luồng đồng thời và một biến đếm đối tượng chia sẽ. Mỗi một luồng điều khiển một cổng và thưc hiện tăng biến đếm đối tượng chia sẽ khi có một người qua cổng vào siêu thị. Chương trình tương tranh được xây dựng có bốn lớp bao gồm:
(i) Lớp NUMBERCANVAS có dạng của lớp CANVAS trong ngôn ngữ Java, dùng để vẽ một số nguyên lên màn hình.
(ii) Lớp COUNTER là lớp quản lý các đối tượng chia sẽ. Lớp này sử dụng đối tượng display của lớp Numbercanvas để vẽ và hiển thị giá trị biến đếm đối tượng chia sẽ lên màn hình.
(iii) Lớp TURNSTILE có dạng của Thread trong ngôn ngữ Java, lớp này dùng để quản lý hai luồng tương ứng với ba cửa tây và cửa đông. Lớp TURNSTILE sử dụng đối tượng display của lớp Numbercanvas để vẽ và hiển thị giá trị số khách hiện thời vào mỗi cổng tương ứng với mỗi luồng.
(iv) Lớp SUPERMARKET có dạng của Applet như trong ngôn ngữ Java, lớp này dùng để quản lý cả hệ thống siêu thị. Lớp SUPERMARKET sử dụng các đối tượng eastD, westD và counterD của lớp NumberCanvas để vẽ nên giao diện chương trình.
Các phương thức và mối quan hệ giữa các lớp được miêu tả trong biểu đồ lớp như hình 3.2
Hình 3.2.Biểu đồ lớp thiết kế của Supermarket.
Một số phương thức cơ bản mà luận văn áp dụng được miêu tả như sau:
(i) Lớp NumberCanvas dùng để vẽ một số nguyên lên màn hình, các phương thức của lớp NumberCanvas được miêu tả trong hình 3.3.
public void setcolor(Color c){
setBackground(c); repaint();
}
public void setvalue(int newval){
value = newval; repaint();
}
Hình 3.3. Lớp NumberCanvas.
Trong đó, phương thức setcolor(Color c) dùng để thiết lập màu hình nền, phương thức này được đặc tả như hình 3.4.
Hình 3.4. Phương thức setcolor.
Còn phương thức void setvalue(int newval) dùng để hiển thị một số nguyên lên màn hình. Phương thức này được đặc tả như hình 3.5.
Hình 3.5. Phương thức setvalue.
(ii) Lớp Counter là lớp các đối tượng chia sẽ, lớp này có phương thức increment() được miêu tả như hình 3.6 nhằm thực hiện việc đọc và ghi lên biến đếm đối tượng chia sẽ.
Biến value có giá trị kiểu nguyên để lưu giữ giá trị hiện thời của biến chia sẽ.
Public class NumberCanvas extends Canvas {
Public NumberCanvas(String title){…} Public void setcolor(Color c){…} Public void setvalue(int newval){…} }
class Counter { int value = 0; NumberCanvas display; Counter(NumberCanvas t) { display = t; display.setcolor(Color.cyan); }
public void increment() {
int temp = value; //read value Simulate.interrupt();
++temp; //add1 value = temp; //write value display.setvalue(value); }
}
class Simulate {
public static void HWinterrupt() {
if (Math.random()< 0.5) Thread.yield(); } }
Đối tượng display của lớp NumberCanvas để hiển thị giá trị của value lên màn hình.
Ở hình 3.6, phương thức Hwinterrupt() được gọi đến trong phương thức increment() là phương thức của lớp Simulate, phương thức này khi được gọi thỉnh thoảng gây ra sự chuyển đổi luồng bằng cách gọi Thread.yield() và đôi khi bỏ qua lời gọi thoát khỏi luồng hiện tại đang chạy. Ý tưởng nhằm tạo ra một ngắt cứng giữa việc đọc và viết vào biến đối tượng chia sẽ khi đang biểu diễn sự tăng lên của biến này. Do đó sự chuyển đổi luồng có thể xảy ra ở bất kỳ thời điểm nào. Lớp Simulate được miêu tả như hình 3.7.
Hình 3.6. Lớp Counter và phương thức increment.
class Turnstile extends Thread {
NumberCanvas display; int count = 0; int delay;
Counter people;
boolean suspended = true; public void run() {
while(true) { mysuspend(); try { Thread.sleep(delay); } catch(InterruptedException e){ } count++; display.setvalue(count); people.increment(); } } }
(iii) Luồng Turnstile như chỉ ra trong hình 3.8 nhằm mô phỏng sự xuất hiện định kỳ của khách vào siêu thị bằng cách ngủ một khoảng thời gian delay, sau đó gọi phương thức increment() của lớp Counter để tăng biến đếm đối tượng chia sẽ.
Hình 3.8.Lớp Turnstile.
Hình 3.9. Phương thức mysuspend.
synchronized void mysuspend() {
while (suspended){ try {
wait();
}catch (InterruptedException e){ }
} }
public void activate() { if (suspended) { suspended = false; display.setcolor(Color.White); synchronized(this) { notify(); } } }
Phương thức mysuspend() gọi phương thức wait() được miêu tả như hình 3.9 nhằm đảm bảo rằng luồng (Luồng đang khóa đối tượng) đi vào trạng thái ngừng hoạt động và đang chờ một sự kiện nào đó xảy ra, đồng thời đối tượng tạm thời được mở khóa và cho phép các luồng khác trong hàng đợi sử dụng đối tượng được hoạt động. Nếu không có phương thức wait() thì NumberCanvas không biết khi nào cập nhật hiển thị, nên khi chạy chương trình hai luồng tự động thực hiện tăng biến đếm chia sẽ luôn chứ không chờ sự kiện nút Start được chọn.
Phương thức activate() của lớp Turnstile như trong hình 3.2 gọi phương thức notify() được miêu tả như hình 3.10 nhằm làm cho một luồng đang chờ sự thay đổi từ đối tượng (Luồng đã gọi wait()) sẽ được thực thi tiếp.
Hình 3.10. Phương thức activate.
Phương thức passivate() được miêu tả như hình 3.11 nhằm tạm hoãn luồng đang thực hiện khi nút Stop được kích chọn.
Hình 3.11. Phương thức passivate.
Nhằm thực hiện tăng và hiển thị giá trị biến đếm đối tượng chia sẽ cứ mỗi khi có một vị khách vào siêu thị, luồng Turnstile đưa ra phương thức run()
public void passivate() { if (!suspended) {
suspended = true;
display.setcolor(Color.white); }
được miêu tả như hình 3.12. Biến count có giá trị kiểu nguyên được dùng để đếm số khách vào ở mỗi cổng, biến people là một đối tượng của lớp Counter.
Hình 3.12. Phương thức run.
(iv) Trong hình 3.2 lớp Supermarket là một Applet tương tự như một Applet trong Java, lớp này có các phương thức được chỉ ra như trong hình 3.2.
Hình 3.13. Phương thức handleEvent.
public void run() {
while(true) { mysuspend(); try{ Thread.sleep(delay_); } catch(InterruptedException e) { } count++; display.setvalue(count); people.increment(); } }
public boolean handleEvent(Event event) { if (event.id != event.ACTION_EVENT) { return super.handleEvent(event);
} else if (event.target == start) { east.activate();
west.activate(); south.activate(); return true;
} else if (event.target == stop_) { stop();
return true;
} else return super.handleEvent(event); }
Phương thức handleEvent(event:Event) của lớp Supermarket dùng để bắt sự kiện kích chuột trên màn hình, phương thức này xác nhận nút Start hay nút Stop được chọn lựa. Nếu nút Start được chọn thì gọi đến phương thức activate() để kích hoạt luồng và nếu nút Stop được chọn thì gọi đến phương thức stop() làm cho luồng tạm dừng hoạt động. Đoạn mã cho phương thức này được minh họa trong hình 3.13.
Phương thức init() dùng để tạo Panel gồm có counterD, westD, eastD và southD là các đối tượng của lớp NumberCanvas với các nhãn Counter, West, East và South tương ứng, cùng với textbox hiển thị giá trị của bộ đếm counter, ba luồng west, east và south và hai button Start và Stop như hình 3.14.
Hình 3.14. Phương thức init.
public void init() {
super.init();
this.setFont(new Font("Helvetica",Font.BOLD,24)); Panel p1 = new Panel();
p1.add(start = new Button("Start")); p1.add(stop = new Button("Stop")); Panel p2 = new Panel();
NumberCanvas counterD = new NumberCanvas("Counter"); NumberCanvas westD = new NumberCanvas("West"); NumberCanvas eastD = new NumberCanvas("East"); NumberCanvas southD = new NumberCanvas("South"); counterD.resize(150,100);
westD.resize(100,100); eastD.resize(100,100);
southD.resize(100,100);
p2.add(westD); p2.add(counterD); p2.add(eastD); setLayout(new BorderLayout());
add("Center",p2); add("North",p1); Counter people = new Counter(counterD); west = new Turnstile(westD, people, 500); east = new Turnstile(eastD, people, 500); south = new Turnstile(southD, people, 500); west.start(); east.start(); south.start(); }
Với các lớp và các phương thức được xây dựng như miêu tả ở trên, giao diện của chương trình khi chạy chương trình được đưa ra như trong hình 3.15.
Hình 3.15. Giao diện chương trình Supermarket.
Khi nút Start được kích chọn, ba luồng east, west và north được tạo ra bởi phương thức init() chuyển từ trạng thái chờ (wait) sang trạng thái hoạt động (activate), mỗi một luồng tự động tăng biến đếm số khách vào mỗi cổng tương ứng và gọi phương thức display.setvalue(val) của lớp NUMBERCANVAS để hiển thị chúng lên màn hình. Hình 3.16 đưa ra một kết quả ngẫu nhiên khi ta kích chọn nút Stop.
Hình 3.16. Kết quả thử nghiệm chương trình Supermarket.