Giáo trình Lập trình Java (Phần 2)
Một phần tử (element) GUI được thiết lập bằng cách sử dụng thủ tục sau:
Tạo đối tượng
Xác định sự xuất hiện ban đầu của đối tượng
Chỉ ra nó nằm ở đâu
Thêm phần tử vào giao diện trên màn hình
Một thành phần (component) GUI là một đối tượng trực quan. Người dùng tương
tác với đối tượng này thông qua con trỏ chuột hay bàn phím. Các thành phần như là
button, label có thể được nhìn thấy trên màn hình. Bất kỳ cái gì chung cho tất cả
các thành phần GUI đều được tìm thấy trong lớp Component. Để tạo các đối tượng
GUI chúng ta cần nhập gói java.awt.
AWT là viết tắt của Abstract Windowing Toolkit. AWT là một bộ các lớp trong
Java cho phép chúng ta tạo GUI và chấp nhận các nhập liệu của người dùng thông qua
bàn phím và chuột. AWT cung cấp các thành phần khác nhau để tạo GUI hiệu quả và
lôi cuốn người sử dụng. Các thành phần này có thể là:
Vật chứa (Container)
Thành phần (Component)
Trình quản lý cách trình bày (Layout manager)
Đồ họa (Graphic) và các tính năng vẽ (draw)
Phông chữ (Font)
Sự kiện (Event)
Gói AWT chứa các lớp, giao diện và các gói khác. Hình sau đây mô tả một phần
nhỏ của hệ thống phân cấp lớp AWT.
Trang 1
Trang 2
Trang 3
Trang 4
Trang 5
Trang 6
Trang 7
Trang 8
Trang 9
Trang 10
Tải về để xem bản đầy đủ
Tóm tắt nội dung tài liệu: Giáo trình Lập trình Java (Phần 2)
ln(" "); } } class Source implements Runnable{ int number; Target target; Thread t; 238 public Source(Target targ,int n){ target = targ; number = n; t = new Thread(this); t.start(); } // đồng bộ gọi phương thức display() public void run(){ synchronized(target) { target.display(number); } } } class Synchblock { public static void main(String args[]){ Target target = new Target(); int digit = 10; Source s1 = new Source(target,digit++); Source s2 = new Source(target,digit++); Source s3 = new Source(target,digit++); try{ s1.t.join(); s2.t.join(); s3.t.join(); }catch(InterruptedException e){ System.out.println("Interrupted"); } } } 239 Ở đây, từ khóa synchronized không hiệu chỉnh phương thức “display()”. Từ khóa này được sử dụng trong phương thức run() của lớp “Target”. Kết quả xuất ra màn hình tương tự với kết quả chỉ ra ở hình số 6.6 6.10.3. Ƣu điểm của các phƣơng thức đồng bộ Người lập trình thường viết các chương trình đơn luồng. Tất nhiên một số trường hợp nhất định đa luồng là không hiệu quả. Ví dụ nó không làm tăng hiệu năng của các trình biên dịch. Trình biên dịch Java Sun không chứa nhiều phương thức đồng bộ. Các phương thức đồng bộ không thực thi tốt như là các phương thức không đồng bộ. Các phương thức này chậm hơn từ ba đến bốn lần so với các phương thức tương ứng không đồng bộ. Trong trường hợp chúng ta cần hiệu năng cao thì nên hạn chế sử dụng các phương thức đồng bộ. 6.11. Cơ chế đợi thông báo Luồng chia các tác vụ thành các đơn vị cụ thể và logic. Điều này thay thế các hình thức lập trình lặp sự kiện. Các luồng loại trừ “polling” (kiểm tra liên tục). Một vòng lặp dùng để kiểm tra điều kiện gọi là “polling”. Khi điều kiện nhận giá trị là True (đúng), các câu lệnh tương ứng được thực hiện. Đây là tiến trình thường lãng phí thời gian của CPU. Ví dụ khi một luồng sinh ra một số dữ liệu và các luồng khác đang chi phối nó luồng sinh ra phải đợi cho đến khi các luồng sử dụng nó hoàn thành trước khi phát sinh ra dữ liệu. Để tránh trường hợp polling, Java bao gồm một cơ chế giao tiếp giữa các tiến trình bằng các phương thức “wait()”, “notify()” và “notifyAll()” . Các phương thức này được thực hiện như là các phương thức final trong lớp Object, vì vậy tất cả các lớp có thể thâm nhập chúng. Tất cả 3 phương thức này có thể được gọi chỉ từ trong phạm vi một phương thức đồng bộ (synchronized). Các chức năng của phương thức “wait()”, “notify()”, và “notifyAll()” là: Phương thức wait() làm cho luồng gọi nó từ bỏ yêu cầu monitor, và chuyển sang trạng thái “sleep” (chờ) cho đến khi luồng khác thôi monitor tài nguyên nó cần (đối tượng đang monitor gọi phương thức “notify()”). Phương thức notify() đánh thức, hoặc thông báo cho luồng đầu tiên mà đã gọi phương thức wait() trên cùng đối tượng. Phương thức notifyAll() đánh thức, hoặc thông báo tất cả các luồng mà đã gọi phương thức wait() trên cùng đối tượng. Luồng có quyền ưu tiên cao nhất là luồng chạy đầu tiên. Cú pháp của 3 phương thức này như sau: 240 final void wait() throws IOException final void notify() final void notifyAll() Các phương thức wait() và notify() cho phép chia sẻ đối tượng, làm tạm ngừng luồng, khi đối tượng trở thành không còn giá trị cho luồng. Chúng cũng cho phép luồng tiếp tục khi thích hợp. Các luồng bản thân nó không bao giờ kiểm tra trạng thái của đối tượng đã chia sẻ. Một đối tượng mà điều khiển các luồng yêu cầu nó theo kiểu này được gọi là monitor. Trong phạm vi Java, một monitor là bất kỳ đối tượng nào mà có mã đồng bộ. Các monitor được sử dụng cho các phương thức wait() và notify(). Cả hai phương thức này phải được gọi trong mã đồng bộ. Một số điểm cần nhớ trong khi sử dụng phương thức wait(): Luồng gọi trả CPU Luồng gọi mở khóa Luồng gọi đi vào vùng đợi của monitor. Các điểm chính cần nhớ về phương thức notify() Một luồng vùng đợi của monitor chuyển sang trạng thái sẵn sàng. Luồng mà đã được thông báo phải yêu cầu khóa monitor trước khi nó có thể bắt đầu. Phương thức notify() là không chính xác, vì nó không thể chỉ ra được luồng được thông báo. Trong một trạng thái đã trộn lẫn, luồng có thể thay đổi trạng thái của monitor mà điều này làm ảnh hưởng đến luồng đã được đưa thông báo. Trong trường hợp này, các phương thức của monitor đưa ra 2 sự đề phòng: o Trạng thái của monitor sẽ được kiểm tra trong một vòng lặp “while” thay vì là câu lệnh if o Sau khi thay đổi trạng thái của monitor, phương thức notifyAll() nên được sử dụng thay vì notify(). Ví dụ 6.7: Biểu thị cho việc sử dụng các phương thức notify(0 và wait(): import java.applet.*; import java.awt.*; import java.awt.event.*; /* */ public class mouseApplet extends Applet implements MouseListener{ boolean click; 241 int count; public void init() { super.init(); add(new clickArea(this)); //doi tuong ve duoc tao ra va them vao add(new clickArea(this));//doi tuong ve duoc tao ra va them vao addMouseListener(this); } public void mouseClicked(MouseEvent e) { } public void mouseEntered(MouseEvent e) { } public void mouseExited(MouseEvent e) { } public void mousePressed(MouseEvent e) { synchronized (this) { click = true; notify(); } count++; //dem viec click Thread.currentThread().yield(); click = false; } public void mouseReleased(MouseEvent e) { } } //kết thúc Applet class clickArea extends java.awt.Canvas implements Runnable{ mouseApplet myapp; clickArea(mouseApplet mapp){ this.myapp = mapp; setSize(40,40); 242 new Thread(this).start(); } public void paint(Graphics g){ g.drawString(new Integer(myapp.count).toString(),15,20); } public void run(){ while(true){ synchronized (myapp) { while(!myapp.click){ try{ myapp.wait(); }catch(InterruptedException ie){ } } } repaint(250); } }//end run } Không cần các phương thức wait() và notify(), canvas không thể biết khi nào cập nhập hiển thị. Kết quả xuất ra ngoài của chương trình được đưa ra như sau: Hình 6.7. Kết quả sau mỗi lần kích chuột 243 6.12. Khoá chết (Deadlock) Một “deadlock” xảy ra khi hai luồng có một phụ thuộc vòng trên một cặp đối tượng đồng bộ; ví dụ, khi một luồng thâm nhập vào monitor trên đối tượng “ObjA”, và một luồng khác thâm nhập vào monitor trên đối tượng “ObjB”. Nếu luồng trong “ObjA” cố gắng gọi phương thức đồng bộ trên “ObjB” khoá chết xảy ra. Khó để tìm ra khóa chết bởi những nguyên nhân sau: Nó hiểm khi xảy ra, khi hai luồng chia nhỏ thời gian thực thi cùng lúc Nó liên quan đến nhiều hơn hai luồng và hai đối tượng đồng bộ Nếu một chương trình đa luồng bị treo thường xuyên, ngay lập tức kiểm tra lại điều kiện gây ra khoá chết. Ví dụ 6.8:sau tạo ra điều kiện khoá chết. Lớp chính bắt đầu 2 luồng. Mỗi luồng gọi phương thức đồng bộ run(). Khi luồng “t1” thức dậy, nó gọi phương thức “synchIt()” của đối tượng deadlock “dlk2”. Khi luồng “t1” monitor “dlk2”, luồng “t1” bắt đầu đợi monitor. Khi luồng “t2” thức, nó cố gắng gọi phương thức “synchIt()” của đối tượng Deadlock “dlk1”. Bây giờ, “t2” cũng phải đợi, bởi vì đây là trường hợp tương tự với luồng “t1”. Từ đó, cả hai luồng đang đợi lẫn nhau, cả hai sẽ không bao giờ thức được. Đây là điều kiện khoá chết. public class Deadlock implements Runnable{ public static void main(String args[]){ Deadlock dlk1= new Deadlock(); Deadlock dlk2 = new Deadlock(); Thread t1 = new Thread(dlk1); Thread t2 = new Thread(dlk2); dlk1.grabIt = dlk2; dlk2.grabIt = dlk1; t1.start(); t2.start(); System.out.println("Started"); try{ t1.join(); t2.join(); }catch(InterruptedException e){ System.out.println("error occured"); 244 } System.exit(0); } Deadlock grabIt; public synchronized void run() { try{ Thread.sleep(1500); }catch(InterruptedException e){ System.out.println("error occured"); } grabIt.syncIt(); } public synchronized void syncIt() { try{ Thread.sleep(1500); System.out.println("Sync"); }catch(InterruptedException e){ System.out.println("error occured"); } System.out.println("In the syncIt() method"); } } Kết quả của chương trình này được hiển thị như sau: 245 Hình 6.8. Kết quả thực hiện ví dụ 6.8 6.13. Thu dọn rác Thu dọn “rác” (Garbage collection) cải tạo hoặc làm trống bộ nhớ đã cấp cho các đối tượng mà các đối tượng này không sử dụng trong thời gian dài. Trong ngôn ngữ lập trình hướng đối tượng khác như C++, lập trình viên phải tự giải phóng. Thất bại trong việc giải phóng bộ nhớ có thể gây ra một số hậu quả. Java tự động tiến hành thu dọn rác để cung cấp giải pháp cho vấn đề này. Một đối tượng trở nên thích hợp cho sự dọn rác nếu không có tham chiếu đến nó, hoặc nếu nó được gán bằng null. Trình thực thi dọn rác là một luông chạy ngầm (deamon) co mức ưu tiên thấp. Ta có thể gọi phương thức gc() của thể nghiệm để dọn rác. Tuy nhiên, bạn không thể dự đoán hoặc bảo đảm rằng sự dọn rác sẽ thực thi ngay sau đó. Sử dụng câu lệnh sau để tắt đi sự dọn rác trong ứng dụng: Java –noasyncgc . Nếu chúng ta tắt đi sự dọn rác, chương trình hầu như chắc chắn rằng bị treo do thiếu bộ nhớ. Phƣơng thức finalize() Java cung cấp một phương pháp để làm sạch rác trước khi một luồng, chương trình kết thúc. Điều này tương tự như phương thức Destructor của C++ Phương thức finalize(), nếu có, sẽ được thực thi trên mỗi đối tượng, trước khi sự dọn rác thực hiện. Câu lệnh của phương thức finalize() như sau: protected void finalize() throws Throwable Tham chiếu không phải là rác; chỉ các đối tượng mới gọi là rác Ví du: Object a = new Object(); 246 Object b = a; a = null; Ở đây, nó sẽ sai khi nói rằng “b” là một đối tượng. Nó chỉ là một tham chiếu. Hơn nữa, trong đoạn mã trích trên mặc dù “a‟ được đặt là rỗng, nó không thể được dọn, bởi vì nó vẫn còn có một tham chiếu b đến nó. Vì thế “a” vẫn còn với truy cập được, thật vậy, nó vẫn còn có phạm vi sử dụng trong phạm vi chương trình. Ở đây, nó sẽ không được dọn. Tuy nhiên, trong ví dụ cho dưới đây, giả định rằng không có tham chiếu đến “a” tồn tại, đối tượng “a” trở nên thích hợp cho việc dọn rác. Object a = new Object(); a = null; Một ví dụ khác: Object m = new Object(); Object m = null; Đối tượng được tạo ra ban đầu trở thành đối tượng cần dọn cho bộ dọn rác Object m = new Object(); m = new Object(); Bây giờ, ban đầu sẽ được dọn, và một đối tượng mới tham chiếu bởi “m” đang tồn tại. Bạn có thể chạy phương thức gc() (garbage collection), nhưng không bảo đảm rằng nó sẽ chạy. Ví dụ 6.9: điển hình cho gc(). class GCDemo { public static void main(String args[]) { int i; long a; , 247 Runtime r=Runtime.getRuntime(); long valuesD =new long[200]; System.out.println("Amount of free memory is" + r.freeMemory()); r.gc(); System.out.println("Amount of free memory after garbage collection is " + r.freeMemory()); for (a=10000,i=0;i<200;a++,i++) { values[i] =new long(a); } System.out.println("Amount of free memory after creating the array " + r.freeMemory()); for (i=0;i<200;i++) { values[i] =null; } System.out.println("Arnount of free memory after garbage collection is " + r.freeMemory()); } } Chúng ta khai một mảng gồm 200 phần tử, trong đó kiểu dữ liệu là kiểu long. Trước khi mảng được tạo ra, chúng ta phải xem lượng bộ nhớ trống, và hiển thị nó. Rồi thì chúng ta gọi phương thức gc() của đối tượng Runtime hiện thời. Điều này không chắc sẽ thực thi dọn rác ngay. Rồi chúng ta tạo ra mảng, và gán giá trị cho các phần tử của mảng. Điều này sẽ giảm bớt số lượng bộ nhớ trống. Để làm các phần tử mảng trở thành đối tượng cho bộ thu nhặt rác ta gán chúng bằng null. Cuối cùng, chúng ta sử dụng phương thức gc() để gọi bộ dọn rác lần nữa. Kết quả xuất ra màn hình của chương trình trên như sau: 248 Hình 6. 9. Kết quả chạy ví dụ 6.9 249 Câu hỏi và bài tập chƣơng 6 1. Viết một chương trình mà hiển thị một sự đếm lùi từng giây cho đến không, như hình sau: Ban đầu, số 300 sẽ được hiển thị. Giá trị sẽ được giảm dần cho đến 1 đến khi đạt giá trị 0. Giá trị sẽ được trả lại 300 một lần nữa giảm đến trở thành 0. 2. Viết một chương trình mà hiển thị như hình dưới đây: Tạo 3 luồng và một luồng chính trong “main”. Thực thi mỗi luồng như một chương trình. Khi chương trình kết thúc, các câu lệnh thoát cho mỗi luồng sẽ được hiển thị. Sử dụng kỹ thuật xử lý ngoại lệ. 3. Sử dụng Java Awt, Java Applet, và Thread viết chương trình vẽ một đoạn thẳng màu đỏ trên nền đen chuyển động quy trung quanh một tâm cố định theo mẫu ở hình sau: 250 4. Sử dụng Java Awt, Java Applet, Thread vẽ ra màn hình dòng chữ “WellCome” màu magenta, chạy ngẫu nhiên trên màn hình khi chạm các cạnh sẽ bật trở lại theo mẫu ở hình sau: 5. Sử dụng Java Awt, Java Applet, Thread vẽ ra màn hình dòng chữ “Java Applet” màu xanh, chạy từ trái sang phải theo mẫu ở hình sau: 6. Sử dụng Java Awt, Java Applet, Thread vẽ ra màn hình dòng chữ “Khoa Công Nghệ Thông Tin” chạy từ phải sang trái mẫu ở hình sau: 251 7. Sử dụng Java Awt, Java Applet và Thread viết chương trình tạo hình quả bóng chuyển động ngẫu nhiên trên màn hình, khi chạm vào các cạnh sẽ bật trở lại theo mẫu ở hình sau: 8. Viết một chương trình cho thấy rằng những Thread có độ ưu tiên cao được thực thi, những thread có độ ưu tiên thấp sẽ bị dừng. 9. Viết một chương trình ví dụ cho một thread có độ ưu tiên cao sử dụng Sleep để cho những thread có độ ưu tiên thập sẽ chuyển sang chế độ run. 10. Viết một chương trình mà hiển thị một sự đếm lùi từng giây cho đến không, như hình sau: 11. Viết một chương trình hiển thị như hình dưới đây: 252 Tạo 3 luồng và một luồng chính trong “main”. Thực thi mỗi luồng như một chương trình. Khi chương trình kết thúc, các câu lệnh thoát cho mỗi luồng sẽ được hiển thị. Sử dụng kỹ thuật xử lý ngoại lệ. 253 TÀI LIỆU THAM KHẢO [1]. Trần Tiến Dũng. Giáo Trình Lý Thuyết Và Bài Tập Java. NXB Lao động xã hội. 2005. [2]. Hoàng Đức Hải. Nguyễn Phương Lan. Java Tập 1. NXB Lao động xã hội. 2004. [3]. Hoàng Đức Hải. Nguyễn Phương Lan. Java Tập 2. NXB Lao động xã hội. 2004. [4]. Hồ Trọng Long. Nguyễn Duy Hoàng Mỹ. Nhập Môn Lập Trình Java. NXB Thống Kê. 2005 [5]. Đoàn Thiện Ngân. Cấu trúc dữ liệu với Java. Nhà xuất bản Lao động – Xã hội2005 [6]. Bruce Eckel, Thinking in Java, Prentice-Hall mid-June, 2006. [7]. Horstmann and GaryCornell.Core JavaVolume II--Advanced Features (9th Edition) (Core Series). Publisher2013. [8]. Michael Ernest. Java SE 7 Programming Essentials, Publisher2013 [9]. [10]. [11]. 254
File đính kèm:
- giao_trinh_lap_trinh_java_phan_2.pdf