Lập trình Java cơ bản - Chương 8: Đa luồng

Mục tiêu:

 Sau khi kết thúc chương này, bạn có thể:

 Định nghĩa một luồng (thread)

 Mô tả đa luồng

 Tạo và quản lý luồng

 Hiểu được vòng đời của luồng

 Mô tả một luồng hiểm (daemon thread)

 Giải thích thiết lập các luồng ưu tiên như thế nào

 Giải thích được sự cần thiết của sự đồng bộ

 Hiểu được cách áp dụng vào các từ khoá đồng bộ như thế nào (how to apply synchronized keywords)

 Liệt kê những điểm yếu của sự đồng bộ

 Giải thích vai trò của các phương thức wait() (đợi), notify() (thông báo) và notifyAll().

 Mô tả một điều kiện bế tắc (deadlock condition)

 

doc23 trang | Chuyên mục: Java | Chia sẻ: dkS00TYs | Lượt xem: 2515 | Lượt tải: 2download
Tóm tắt nội dung Lập trình Java cơ bản - Chương 8: Đa luồng, để xem tài liệu hoàn chỉnh bạn click vào nút "TẢI VỀ" ở trên
 lặp “while” tốt hơn là câu lệnh if
Sau khi thay đổi trạng thái của monitor, phương thức notifyAll() sẽ được sử dụng, tốt hơn phương thức notify().
	Chương trình 8.6 biểu thị cho việc sử dụng các phương thức notify(0 và wait():
	Chương trình 8.6 
	import java.applet.*;
import java.awt.*;
import java.awt.event.*;
/* */
public class mouseApplet extends Applet implements MouseListener{
	boolean click;
	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);
	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(), luồng bức vẽ (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 8.8 Kết quả sau mỗi lần kích chuột
Sự bế tắt (Deadlocks)
	Một “deadlock” (sự bế tắt) xảy ra khi hai luồng có một phụ thuộc vòng quanh trên một cặp đối tượng đồng bộ; lấy 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”, một bế tắt xảy ra.
	Nó khó để gỡ lỗi một bế tắ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 trong cùng một con đường
Nó có thể bao hàm 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 khóa kín thường xuyên, ngay lập tức kiểm tra lại điều kiện bế tắt.
	Chương trình 8.7 tạo ra điều kiện bế tắt. Lớp chính (main) bắt đầu 2 luồng. Mỗi luồng gọi phương thức đồng bộ run(). Khi luồng “t1” đánh thức, nó gọi phương thức “synchIt()” của đối tượng deadlock “dlk1”. Từ đó luồng “t2” một mình giám sát cho “dlk2”, luồng “t1” bắt đầu đợi monitor. Khi luồng “t2” đánh thức, nó cố gắng gọi phương thức “synchIt()” của đối tượng Deadlock “dlk2”. 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ẽ đánh thức. Đây là điều kiện bế tắt.
Chương trình 8.7
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 = dlk1;
	dlk2.grabIt = dlk2;
	t1.start();
	t2.start();
	System.out.println("Started");
	try{
	t1.join();
	t2.join();
	}catch(InterruptedException e){
	System.out.println("error occured");
	}
	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:
Hình 8.9 Sự bế tắt
Thu dọn “rác” (Garbage collection)
	Thu dọn “rác” (Garbage collection) cải tạo hoặc làm trống bộ nhớ đã định vị 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 làm cho bộ nhớ trống mà đã không được yêu cầu trong thời gian dài. Tình trạng không hoạt động để bộ nhớ trống có thể là kết quả trong một số vấn đề. Java tự động tiến trình thu dọn rác để cung cấp giải pháp duy nhất 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ó đã đăng ký rỗng.
	Sự dọn rác thực thi như là một luồng riêng biệt có quyền ưu tiên thấp. Bạn có thể viện dẫn một phương thức gc() của thể nghiệm để viện dẫn sự 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 một cách trọn vẹn sau đó.
	Sử dụng câu lện 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 bởi việc đó.
Phương thức finalize() (hoàn thành)
	Java cung cấp một con đường để làm sạch một tiến trình trước khi điều khiển trở lại hệ điều hành. Điều này tương tự như phương thức phân hủy của C++
	Phương thức finalize(), nếu hiện diện, sẽ được thực thi trên mỗi đối tượng, trước khi sự dọn rác.
	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à sự dọn rác; chỉ các đối tượng mới được dọn rác
	Lấy thể nghiệm:
	Object a = new Object();
	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 đối tượng 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 rác, 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 đến được, thật vậy, nó vẫn còn có phạn vi sử dụng trong phạm vi chương trình. Ở đây, nó sẽ không được dọn rác.
	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 garbage collection.
	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 trong sự bắt đầu có hiệu lực cho garbage collection
	Object m = new Object();
	M = new Object();
	Bây giờ, đối tượng căn nguyên có hiệu lực cho garbage collection, 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 garbage collection, nhưng không có banỏ đảm rằng nó sẽ xảy ra.
	Chương trình 8.8 điển hình cho garbage collection.
	Chương trình 8.8
	class GCDemo
{
public static void main(String args[])
{
int i;
long a; ,
Runtime r=Runtime.getRuntimeO;
Long valuesD =new Long[200];
System. out. print In ("Amount of free memory is" + r.freeMemoryO);
r.gcO;
System.out.println("Amount of free memory after garbage collection is " + r.freeMemoryO);
for (a=IOOOO.i=O;i<200;a++.i++)
{
values[i] =new Long(a);
}
System.out.println("Amount of free memory after creating the array
" + r.freeMemoryO);
for (i=O;i<200;i++)
{
values[i] =null;
}
System.out.println("Arnount of free memory after garbage collection is
" + r.freeMemoryO);
}
	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 xác định rõ số lượng bộ nhớ trống, và hiển thị nó. Rồi thì chúng ta viện dẫn phương thức gc() của thể nghiệm Runtime (thời gian thực thi) hiện thời. Điều này có thể hoặc không thể thực thi garbage collection. Rồi thì chúng ta tạo ra mảng, và đang ký 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 mảng phần tử thích hợp cho garbage collection, chúng ta đặt chúng rỗng. Cuối cùng, chúng ta sử dụng phương thức gc() để viện dẫn garbage collection lần nữa.
	Kết quả xuất ra màn hình của chương trình trên như sau:
Hình 8.10 Garbage collection
	Tổng kết
Một luồng là đơn vị nhỏ nhất của đoạn mã thực thi được mà một tác vụ riêng biệt.
Đa luồng giữ cho thời gian rỗi là nhỏ nhất. Điều này cho phép bạn viết các chương trình có khả năng sử dụng tối đa CPU.
Luồng bắt đầu thực thi sau khi phương thức start() được gọi
Lập trình viên, máy ảo Java, hoặc hệ điều hành bảo đảm rằng CPU được chia sẻ giữa các luồng.
Có hai loại luồng trong một chương trình Java:
Luồng người dùng
Luồng hiểm.
Một nhóm luồng là một lớp mà nắm bắt một nhóm các luồng.
Đồng bộ cho phép chỉ một luồng thâm nhập một tài nguyên được chia sẻ tại một thời điểm.
Để tránh kiểm soát vòng, Java bao gồm một thiết kế tốt trong tiến trình kỹ thuật truyền thông sử dụng các phương thức “wait()” (đợi), “notify()” (thông báo) và “notifyAll()” (thông báo hết).
Một “bế tắt” xảy ra khi hai luồng có mọt phụ thuộc xoay vòng trên một phần của các đối tượng đồng bộ
Garbage collection là một tiến trình nhờ đó bộ nhớ được định vị để các đối tượng mà không sử dụng trong thời gian dài, có thể cải tạo hoặc làm rãnh bộ nhớ.
Kiểm tra lại sự hiểu biết của bạn
Một ứng dụng có thể chứa đựng nhiều luồng	Đúng/Sai
Các luồng con được tạo ra từ luồng chính	Đúng/Sai
Mỗi luồng trong một chương trình Java được đăng ký một quyền ưu tiên mà máy ảo Java có thể thay đổi.	Đúng/Sai
Phương thức____________ có thể tạm thời ngừng việc thực thi luồng	
Mặc định, một luồng có một quyền ưu tiên ________ một hằng số của _______
_________ luồng được dùng cho các luồng “nền”, cung cấp dụch vụ cho luồng khác.
Trong luồng đồng bộ, một __________ là một đối tượng mà được sử dụng như là một khóa riêng biệt lẫn nhau.
___________ thường thực thi bởi một vòng lặp mà được sử dụng để lặp lại việc kiểm tra một số điều kiện.
Bài tập:
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 ngoài giá trị 0. Giá trị sẽ được trả lại 300 một lần nữa giảm đến trở thành 0.
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 thực thi. 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 nắm bắt lỗi.

File đính kèm:

  • docLập trình Java cơ bản - Chương 8_Đa luồng.doc
Tài liệu liên quan