Bài giảng Lập trình Java - Chương 9: Threads

9.1- Ôn tập.

9.2- Luồng và đa luồng

9.3- Luồng trong Java

9.4- Trạng thái của luồng

9.5- Lập trình luồng trong Java

9.6- Độ ưu tiên của luồng

9.7- Đồng bộ giữa các luồng

9.8- Deadlock

9.9- Cơ chế Chờ-nhận biết

9.10- Tóm tắt

 

ppt49 trang | Chuyên mục: Java | Chia sẻ: dkS00TYs | Lượt xem: 4073 | Lượt tải: 1download
Tóm tắt nội dung Bài giảng Lập trình Java - Chương 9: Threads, để xem tài liệu hoàn chỉnh bạn click vào nút "TẢI VỀ" ở trên
vi sleep(). Luồng bị chờ do hành vi wait(). Luồng bị tường minh nhận hành vi yield(). Luồng bị khóa vì đang chờ I/O Minh họa về độ ưu tiên của luồng class Thread5 extends Thread// Thread4.java { public void run() { Thread Child = new Thread(this); Child.setName("Child thread"); System.out.println("Name of current thread:" + Thread.currentThread().getName()); System.out.println("Piority of current thread:" + Thread.currentThread().getPriority()); System.out.println("Name of child:" + Child.getName()); System.out.println("Priority of child:" + Child.getPriority()); } public static void main (String args[]) { Thread5 t = new Thread5(); t.start(); t.setName("Parent thread"); } } Name of current thread:Parent thread Piority of current thread:5 Name of child:Child thread Priority of child:5 Press any key to continue... Nếu trong main(), thêm dòng t.setPriority (8); trước dòng t.start(); ta có kết qủa là 8 thay vì 5 9.7- Đồng bộ các luồng Tình huống: Có hai luồng t1, t2 cùng truy xuất 1 đối tượng dữ liệu là biến m. t1 muốn đọc biến m còn t2 muốn ghi biến m.  dữ liệu mà t1 đọc được có thể không nhất quán.  Nếu để cho t2 ghi m trước rồi t1 đọc sau thì t1 đọc được dữ liệu nhất quán tại thời điểm đó.  Cần có cơ chế để chỉ cho phép 1 luồng được truy xuất dữ liệu chung (shared data) tại 1 thời điểm.  Kỹ thuật này gọi là “ĐỒNG BỘ HÓA – SYNCHRONIZATION” Kỹ thuật cơ bản về đồng bộ hóa Tạo ra 1 đối tượng quản lý sự đồng bộ của 1 thao tác dữ liệu của các luồng bằng cách thực thi hộ một tác vụ của các luồng mỗi lần chỉ cho 1 luồng bằng từ khóa synchronized Mọi đối tượng luồng đều được đối tượng quản lý này quan sát (MONITOR) bằng cách cho mọi đối tượng luồng có dữ liệu là đối tượng monitor này và thay vì phải làm 1 tác vụ thì nhờ đối tượng monitor làm hộ hoặc là 1 biến boolean để nhận biết đã có 1 luồng đang thực thi.  Luồng đang được chiếu cố gọi là luồng đang có monitor Minh họa về đồng bộ các luồng bằng MONITOR // Monitor1.java – Lớp làm nhiệm vụ xuất hộ 1 số num class Monitor1 { synchronized void Display (int num) { System.out.println("Output " + num + " - done."); try { Thread.sleep(500); // current thread sleep 1/2 sec } catch (InterruptedException e) { System.out.println ("Thread is interrupted!"); } } } Chương trình sau sẽ xuất 3 số 10,11, 12 ra màn hình, mỗi số được 1 luồng thực thi. Từ khóa synchronized khai báo có quản lý việc đồng bộ các luồng Minh họa về đồng bộ các luồng bằng MONITOR class OutNum implements Runnable // luồng { Monitor1 monitor; // Luồng có dữ liệu là monitor int number; // dữ liệu cần xuất Thread t; // hành vi xuất n với Monitor1 có tên moni OutNum(Monitor1 moni, int n ) { monitor= moni; number = n; t = new Thread(this); t.start(); } // khi luồng chạy, số number được xuất bởi monitor public void run() { monitor.Display(number); } } class Synchro // lớp của chương trình chính { public static void main (String args[]) { Monitor1 monitor = new Monitor1(); int num = 10; OutNum Obj1 = new OutNum(monitor,num++); OutNum Obj2 = new OutNum(monitor,num++); OutNum Obj3 = new OutNum(monitor,num++); // wait for 3 threads to end try { Obj1.t.join(); Obj2.t.join(); Obj3.t.join(); } catch(InterruptedException e) { System.out.println ("Thread was interrupted!"); } } } Minh họa về đồng bộ các luồng bằng MONITOR 3 luồng có 3 trị khác nhau là 10,11, 12 nhưng có chung 1 monitor Ba luồng cùng đơ Output 10 - done. Output 11 - done. Output 12 - done. Press any key to continue.... Kỹ thuật đồng bộ luồng theo khối Đồng bộ một khối tác vụ. Người lập trình có thể không muốn dùng các synchronized method để đồng bộ truy xuất đến đối tượng. Các lớp được cung cấp bởi các thư viện hay do “một ai đó” cung cấp – lớp đã xây dựng- nên không thể thêm từ khóa synchonized vào được các method này. Kỹ thuật đồng bộ luồng theo khối Cú pháp đồng bộ khối synchronized (Object) { } Buộc phải có { } dù chỉ có 1 phát biểu Minh họa đồng bộ khối class Monitor2 // Monitor2.java { void Display (int num) { System.out.println("Output " + num + " - done."); try { Thread.sleep(500); // current thread sleap 1/2 sec } catch (InterruptedException e) { System.out.println ("Thread is interrupted!"); } } } Chương trình sau viết lại chương trình trước, bỏ qua từ khóa synchronized trong lớp Monitor1 ( ở đây gọi là lớp Monitor2) Minh họa đồng bộ khối class Synchro { public static void main (String args[]) { Monitor2 monitor = new Monitor2(); int num = 10; OutNum Obj1 = new OutNum(monitor,num++); OutNum Obj2 = new OutNum(monitor,num++); OutNum Obj3 = new OutNum(monitor,num++); // wait for 3 threads to end try { Obj1.t.join(); Obj2.t.join(); Obj3.t.join(); } catch(InterruptedException e) { System.out.println ("Thread was interrupted!"); } } } Minh họa đồng bộ khối class OutNum implements Runnable { Monitor2 monitor; int number; Thread t; OutNum(Monitor2 moni, int n ) { monitor= moni; number = n; t = new Thread(this); t.start(); } public void run() { synchronized (monitor) { monitor.Display(number); } } } Minh họa đồng bộ khối 9.8- Deadlock Deadlock – tình huống bế tắc, đóng băng- xẩy ra khi các luồng chờ tài nguyên (monitor) của nhau hình thành một chu trình. Deadlock hiếm khi xẩy ra. Minh họa: DeadlockDemo.java Giải thích DeadlockDemo class 1 ứng dụng có 2 luồng :Luồng t1 trong đối tượng d1, luồng t2 trong đối tượng d2 Monitor của t1 lại là d2 và monitor của t2 lại là d1 (tréo nhau). Cả 2 luồng cùng gọi hành vi synchronized run() và cùng ngủ 300 mili giây.Vì chia sẻ thời gian CPU nên t1 ngủ trước và t2 ngủ sau (xem phương thức run()). Khi t1 thức dậy (wake up), phương thức Synchro() của đối tượng monitor của d2 (chứa luồng t2) được gọi nhưng luồng t2 đang ngủ nên phương thức này chưa thể thực thi. Khi t2 thức dậy (wake up), phương thức Synchro() của đối tượng monitor của d1 (chứa luồng t1) được gọi nhưng luồng t1 cũng đang ngủ nên phương thức này chưa thể thực thi. Như vậy chương trình sẽ đóng băng (blocked) không làm gì được nữa. 9.9- Cơ chế chờ- nhận biết Java cung cấp sẵn một cơ chế giao tiếp liên qúa trình (inter-process mechanism) để các luồng có thể gọi nhau (yêu cầu nhau) sử dụng các final methods của lớp Object: wait() , notify() , notifyAll(). Như vậy mọi lớp đều có thể sử dụng chúng và các phương thức này chỉ có thể được gọi trong các synchronized methods. Cơ chế wait-notify Phương thức wait() : Luồng nhả monitor để đi vào trạng thái sleep cho đến khi 1 luồng khác vào cùng monitor và gọi phương thức notify. Phương thức notify() : Luồng thức dậy (wake up) và nhận biết (notify) rằng luồng thứ nhất đã gọi wait(). Phương thức notifyAll() : Đánh thức tất cả các luồng đang ngủ để chúng biết rằng luồng hiện hành đã gọi phương thức wait(). Khi tất cả các luồng đang ngủ thức dậy, luồng có ưu tiên cao nhất sẽ nắm giữ monitor và thực thi. Chú ý đối với phương thức wait Luồng gọi phương thức wait() sẽ nhả CPU, thôi không dùng CPU nữa. Luồng gọi phương thức wait() sẽ nhả monitor, thôi không khóa (lock) monitor nữa. Luồng gọi phương thức wait() sẽ được đưa vào danh sách hàng đợi monitor (monitor waiting pool) Chú ý đối với phương thức notify Một luồng đang ngủ được đưa ra khỏi monitor waiting pool và đi vào trạng thái ready. Luồng vừa thức giấc (notify) phải giành lại monitor và khóa monitor lại không cho luồng khác chiếm để luồng này được thực thi. Chú ý đối với phương thức notifyAll Luồng đang thực thi cảnh báo cho tất cả các luồng đang ngủ rằng “Tôi đi ngủ đây, các bạn dậy để làm việc”. Luồng ở đầu danh sách monitor waiting pool được vào trạng thái ready Bài toán 5 triết gia ăn tối với 5 chiếc đũa 9.10- Tóm tắt Luồng là biện pháp chia công việc thành các đơn vị cụ thể (concrete) nên có thể được dùng để thay thế vòng lặp. Lập trình đa luồng làm tăng hiệu suất CPU trên những hệ thống “bận rộn”. Tuy nhiên hiệu suất của từng ứng dụng lại bị giảm đang kể (chậm ba bốn lần do các tác vụ đồng bộ hóa), qúa trình biên dịch cũng chậm vì trình biên dịch phải tính toán cơ chế quản lý các luồng. Do vậy trong caùc ứng dụng đòi hỏi yếu tố hiệu suất thời gian là quan trọng, nên tránh sử dụng kỹ thuật đồng bộ hóa.  Nhiều lập trình viên không thích lập trình đa luồng mà chỉ dùng lập trình lập trình đơn luồng để tăng hiệu suất của ứng dụng. Java cung cấp kỹ thuật lập trình đa luồng bằng lớp Thread và interface Runnable. Khi 1 ứng dụng Java thực thi, có 1 luồng đang chạy đó là luồng chính (main thread). Luồng chính rất quan trọng vì (1) Đây là luồng có thể sinh ra các luồng con, (2) Quản lý việc kết thúc ứng dụng vì luồng main chỉ kết thúc khi tất cả các luồng con của nó đã kết thúc. Tóm tắt Hiện thực 1 luồng bằng 1 trong 2 cách: Hiện thực 1 lớp con của lớp Thread, override phương thức run() của lớp này. Khai báo lớp mà ta xây dựng là implement của interface Runnable và định nghĩa phương thức run(). Mỗi java thread có 1 độ ưu tiên từ 1 (MIN) đến 10 (MAX) với 5 là trị mặc định. JVM không bao giờ thay đổi độ ưu tiên của luồng. Có 8 contructor của lớp Thread nhưng 2 constructor thường dùng: Thread() và Thread(String TênLuồng), Thread(ĐốiTượngChứa). Các phương thức Thread.suspend(), Thread.resume(), Thread.stop() không còn được dùng nữa kể từ Java 2. Luồng daemon là luồng chạy ngầm nhằm cung cấp dịch vụ cho các luồng khác. Nếu muốn 1 luồng là daemon, hãy dùng public final void setDeamon (boolean) và kiểm tra 1 luồng có là daemon hay không, hãy dùng public final boolean isDaemon(). Tóm tắt Dữ liệu có thể bị mất nhất quán(hư hỏng) khi có 2 luồng cùng truy xuất dữ liệu tại cùng 1 thời điểm. Đồng bộ là 1quá trình bảo đảm tài nguyên (dữ liệu, file,…) chỉ được 1 luồng sử dụng tại 1 thời điểm. Tuy nhiên, chi phí cho việc này lại làm giảm hiệu suất thời gian của ứng dụng xuống 3, 4 lần. Phương thức wait() sẽ làm 1 luồng đi vào trạng thaí ngủ. Phương thức notify() sẽ đánh thức luồng thứ nhất trong danh sách luồng đang chờ trên cùng 1 đối tượng monitor. Phương thức notifyAll() sẽ đánh thức tất cả các luồng trong danh sách luồng đang chờ trên cùng 1 đối tượng monitor. Deadlock xẩy ra khi 2 luồng có sự phụ thuộc vòng trên một cặp đối tượng quản lý việc đồng bộ (synchronized object). Xin cám ơn 

File đính kèm:

  • pptBài giảng Lập trình Java - Chương 9_Threads.ppt
Tài liệu liên quan