19 thg 3, 2010

Giới thiệu về Java Threads - lập trình đa luồng trong Java

Giới thiệu về Threads-lập trình đa luồng trong Java


ThreadCó một định nghĩa về thread như sau :



Thread là một dòng các điều khiển trong một process hay một ứng dụng. Nguyên văn là : Threads are multiple flows of control within a single program or process.



Với cơ chế multithreading ứng dụng của ta có thể thực thi đồng thời nhiều dòng lệnh cùng lúc. Có nghĩa là ta có thể làm nhiều công việc đồng thời trong cùng một ứng dụng của ta. Có thể hiểu một cách hết sức đơn giản : hệ điều hành với cơ chế đa nhiệm cho phép nhiều ứng dụng chạy cùng lúc. Thì với cơ chế đa luồng, mỗi ứng dụng của ta có thể thực hiện được nhiều công việc đồng thời.




Tại sao không dùng nhiều processes , sao không là multiprocessing mà lại cần đến multithreading ?



Câu trả lời đơn giản nhất là: Việc tạo ra và quản lý các process đòi hỏi nhiều tài nguyên của hệ thống (cả ram và CPU) nhiều hơn rất nhiều so với việc tạo ra một thread. Trong khi đó có thể chỉ cần tạo ra một thread để thực hiện song song một công việc hết sức đơn giản cùng với một công việc chính.



Viết một ứng dụng Java trên bất kỳ nền tảng nào. Khi ứng dụng ,ta chạy thì thực sự đã có một bản sao của JVM khởi động và ứng dụng của ta là một thread nếu ta không dùng multithreading hoặc là nhiều threads nếu tadùng multithreading.



Tạo một threadNhư đã nói, mỗi khi chạy một ứng dụng trong java thì đã có một thread. Đây là thread chính, nó thực thi các dóng lệnh trong method : public static void main . Đây là một điểm nhập bắt buộc cho mọi ứng dụng độc lập.



Để tạo ra một thread khác ngoài thread chính trên, Java cung cấp cho chúng ta hai cách :



- Tạo ra một lớp con của lớp Thread (java.lang.Thread)



- Tạo ra một lớp hiện thực interface Runnable



Chúng ta sẽ tìm hiểu lần lược hai cách trên.




Tạo một lớp con của lớp java.lang.Thread



Bạn khai báo như sau :



class A extends Thread {



public void run() {



... // code for the new thread to execute



}



}



...



A a = new A(); // create the thread object




a.start(); // start the new thread executing



...



Với cách này các dòng lệnh sẽ được đặt trong method run. Method này được override method nguyên thuỷ của lớp Thread.



Sau đó ta sẽ tạo ra một đối tượng từ lớp của ta.



Gọi phương thức start từ đối tượng đó. Lúc này thread của ta chính thức được tạo ra và phương thức start sẽ tự gọi method run của tavà thực thi các dòng lệnh mà đã đặc tả.



Chú ý rằng: method start là method của hệ thống, nó có nhiệu vụ cấp phát bộ nhớ, tạo ra một thread và gọi hàm run của ta. Vì thế không nên override phương thức này. Điều này có thể dẫn đến ko tạo được thread.



Hiện thực interface Runnable

Khai báo như sau:



class B extends … implements Runnable {




public void run() {



... // code for the new thread to execute



}



}



...



B b = new B(); // create the Runnable object



Thread t = new Thread(b); // create a thread object



t.start(); // start the new thread




...



Cũng giống như cách trên, dòng lệnh đặt trong method run (có thể gọi đến các phương thức khác, nhưng phải bắt đầu trong phương thức này)



Sau đó tạo một đối tượng B từ lớp đã hiện thực interface Runnable, tạo thêm một đối tượng t của lớp Thread với thông số cho constructor là đối tượng B.



Sau đó khi gọi phương thức t.start() thì chính thức thread được tạo ra và phương thức run sẽ được triệu gọi một cách tự động.



Bạn sẽ hỏi tại cách thứ hai vẫn phải tạo ra một đối tượng Thread. Vậy tại sao lại đưa ra hai cách hiện thực làm gì ?



Câu trả lời là :



- Bản thân ngôn ngữ Java không hỗ trợ đa thừa kế . Bạn chỉ có thể extends từ một lớp duy nhất. Nhưng bạn lại có thể implements cùng lúc nhiều interface. Khi mà lớp của ta đã [extends] một lớp nào đó rồi (vd : Applet), thì chỉ có thể implements Runnable để tạo ra Thread.



- Việc extends lớp Thread có thể dẫn đến rủi ro là bạn override các method start, stop, ... thì có thể làm cho việc tạo thread là không thể.



Một lời khuyên là: nên tạo ra một lớp hiện thực interface Runnable (cách thứ hai) khi muốn tạo ra một Thread. Chương trình sẽ trong sáng và dễ tìm lỗi hơn.


Việc đồng bộ hóa một method là cách tốt nhất để hạn chế việc sử dụng một method tại một thời điểm. Tuy nhiên sẽ có những trường hợp mà bạn không thể đồng bộ hóa một method, chẳng hạn như khi bạn sử dụng một class được cung cấp bởi bên thứ ba. Trong những trường hợp như thế, bạn không được phép truy cập vào định nghĩa lớp, sẽ ngăn bạn sử dụng từ khóa synchronized.



Sử dụng phát biểu được đồng bộ hoá



Một cách khác để sử dụng từ khóa synchronized là sử dụng phát biểu được đồng bộ hóa. Một phát biểu được đồng bộ hóa chứa một block được đồng bộ hóa , mà bên trong đó đặt những đối tượng và những method được đồng bộ hóa. Gọi các method chứa block được đồng bộ hóa xảy ra khi một thread có được monitor của đối tượng.

Mặc dù bạn có thể gọi những method bên trong một block được đồng bộ hóa, việc công bố method phải được thực hiện bên ngoài một block được đồng bộ hóa.

Ví dụ dưới đây chỉ cách làm thế nào để sử dụng một phát biểu được đồng bộ hóa. Ví dụ này cơ bản cũng giống ví dụ trước, tuy nhiên phát biểu được đồng bộ hóa được sử dụng thay vì từ khóa synchronized. Phát biểu này được đặt trong method run() của class MyThread. Phát biểu được đồng bộ hóa sẽ đồng bộ hóa instance của class Parentheses và vì thế ngăn hai thread sử dụng method display() cùng một lúc.


class Parentheses {
void display(String s) {
System.out.print (”(” + s);
try {
Thread.sleep (1000);
} catch (InterruptedException e) {
System.out.println (”Interrupted”);
}
System.out.println(”)”);
}
}

class MyThread implements Runnable {
String s1;
Parentheses p1;
Thread t;
public MyThread (Parentheses p2, String s2) {
p1= p2;
s1= s2;
t = new Thread(this);
t.start();
}
public void run() {
synchronized(p1){
p1.display(s1);
}
}
}

class Demo{
public static void main (String args[]) {
Parentheses p3 = new Parentheses();
MyThread name1 = new MyThread(p3, “Bob”);
MyThread name2 = new MyThread(p3, “Mary”);
try {
name1.t.join();
name2.t.join();
} catch (InterruptedException e ) {
System.out.println( “Interrupted”);
}
}
}


Ở đây, method display() không sử dụng từ khóa synchronized. Thay vào đó, phát biểu được đồng bộ hóa được sử dụng bên trong method run(). Điều này cho kết quả giống với ví dụ trước bởi vì một thread chờ một khoảng thời gian để thread còn lại kết thúc trước khi tiếp tục xử lý.



Giao tiếp giữa các thread



Các thread mở ra cho các lập trình viên một khoảng không mới trong lập trình, nơi mà ở đó những phần của một chương trình thực thi không đồng bộ với nhau, mỗi một xử lý độc lập với những xử lý khác. Tuy nhiên mỗi thread thỉnh thoảng cần tính toán việc xử lý của chúng và vì thế cần có thể giao tiếp với những thread khác trong suốt quá trình xử lý. Các lập trình viên gọi đây là inter-process communication (giao tiếp trong xử lý).

Bạn có thể có các thread giao tiếp với các thread khác trong chương trình của bạn bằng cách sử dụng những method wait(), notify() và notifyAll(). Những method này được gọi từ bên trong một method được đồng bộ hóa. Method wait() nói cho một thread giải phóng monitor và đi vào trạng thái suspend. Có hai dạng method wait() khác nhau. Một dạng không yêu cầu đối số và vì thế một thread sẽ chờ cho đến khi nó được thông báo. Một dạng khác có đối số để bạn xác định khoảng thời gian chờ. Bạn xác định độ dài thời gian trong mili giây và đặt nó vào trong method wait().

Method notify() nói cho một thread đang suspend bởi method wait() và lấy lại điều khiển của monitor. Method notifyAll() đánh thức tất cả các thread đang chờ điều khiển của monitor. Những thread khác chờ trong trạng thái suspend cho đến khi monitor có sẵn trở lại.


Ví dụ dưới đây chỉ cho bạn làm thế nào để sử dụng những method này trong một ứng dụng. Mục đích của chương trình là có một class Pulishser cho một giá trị cho class Consumer thông qua sử dụng class Queue. Ví dụ này định nghĩa bốn class, class Pulisher, class Comsumer, class Queue và class Demo. Class Queue định nghĩa hai instance: exchangeValue và một biến cờ. exchangeValue đặt vào một giá trị trong queue bởi publisher. Biến cờ được sử dụng như một cách đánh dấu giá trị được đặt vào trong queue. Class Queue cũng định nghĩa một method get() và một method put(). Method put() sử dụng để đặt một giá trị vào queue (gán một giá trị cho exchangeValue), method get() sử dụng để nhận giá trị chứa trong queue (trả về giá trị của exchangeValue. Khi một giá trị được gán, method put() thay đổi giá trị của biến cờ từ false thành true xác định một giá trị được đặt vào trong queue. Chú ý giá trị của biến cờ được sử dụng như thế nào trong method get() và method put() để có thread gọi method chờ cho đến khi có một giá trị trong queue hoặc không có giá trị nào trong queue, phụ thuộc vào method nào đang được gọi.

Class Publisher công bố một instance của class Queue và sau đó gọi method put() đặt vào năm số nguyên integer trong queue. Mặc dù method put() được đặt trong một vòng lặp for, mỗi số nguyên integer được đặt vào trong queue, và sau đó có một khoảng tạm dừng cho đến khi số nguyên integer được nhận bởi class Consumer.

Class Consumer tương tự như thiết kế class Publisher, ngoại trừ class Consumer gọi method get() năm lần bên trong một vòng lặp for. Mỗi lần gọi, method get() tạm dừng cho đến khi class Publisher đặt một số nguyên integer vào trong queue.

Method main() của class Demo tạo ra các instance của class Publisher, class Consumer và class Queue. Chú ý rằng các khởi dựng của của class Publisher và class Consumer đều đặt vào một reference đến instance của class Queue. Chúng sử dụng instance của class Queue cho inter-process communication.

Dưới đây là những gì mà bạn sẽ thấy khi chạy chương trình và mã nguồn của ví dụ

Put: 0

Get: 0

Put: 1

Get: 1

Put: 2

Get: 2

Put: 3

Get: 3

Put: 4

Get: 4


class Queue {
int exchangeValue;
boolean busy = false;
synchronized int get() {
if (!busy)
try {
wait();
} catch (InterruptedException e) {
System.out.println(
“Get: InterruptedException”);
}
System.out.println(”Get: ” + exchangeValue);
notify();
return exchangeValue;
}
synchronized void put (int exchangeValue) {
if (busy)
try {
wait();
} catch (InterruptedException e) {
System.out.println(
“Put: InterruptedException”);
}
this.exchangeValue = exchangeValue;
busy = true;
System.out.println(”Put: ” + exchangeValue);
notify();
}
}

class Publisher implements Runnable {
Queue q;
Publisher(Queue q) {
this.q = q;
new Thread (this, “Publisher”).start();
}
public void run() {
for (int i = 0; i < 5; i++){
q.put(i);
}
}
}

class Consumer implements Runnable {
Queue q;
Consumer (Queue q) {
this.q = q;
new Thread (this, “Consumer”).start();
}
public void run() {
for (int i = 0; i < 5; i++){
q.get();
}
}
}

class Demo {
public static void main(String args []) {
Queue q = new Queue ();
new Publisher (q);
new Consumer (q);
}
}




16 thg 3, 2010

Facebook | Tuan Nx, Một ngày se lạnh thú vị

Một chiều thu, thời tiết se lạnh… ảnh hưởng bão mà. Không đến mức phải mặc áo thật dầy, nhưng cũng đủ để ngta phải cuống quít tìm cái áo thu để mặc. Thời tiết thế này, thik nhất được ngồi trà nóng vỉa hè với mấy anh em, tán gẫu ba chuyện lăng nhăng nhưng khá thú vị :D.

Sẽ chẳng có j để viết tiếp nếu như chiều nay…không có cuộc gặp gỡ bất ngờ đó, không có vụ phải đợi đến 30’ trong tâm trạng vừa háo hức + lo lắng cho một người chỉ mới gặp lần đầu….

Mấy anh em đang nói chuyện, thì bỗng có một nhỏ đi ngang qua. Dáng người, khuôn mặt cũng không có j ấn tượg lắm, bởi lẽ cũng chỉ là những người đi lại trên đường thui. Nhưng đột nhiên nhỏ ngồi rụp xuống ôm bụng. Sẵn tính ga-lăng trong người (haha), Hắn tiến đến hỏi han, dìu nhỏ vào ghế ngồi, gọi cho nhỏ cốc trà nóng. Nhỏ thì vẫn ngồi ôm bụng vậy, mấy anh em hỏi han tí rùi lại quay lại câu chuyện của mình. Hắn để ý Nhỏ cứ nhìn đồng hồ lo lắng, Nhỏ hỏi đi xe bus bao nhiu để đến Chùa Láng????(Làm khó mình đây. Đã bj đi bus đến đóa đâu:-s.). Đã ga-lăng thì ga-lăng cho trót, với lại sắc mặt+tình trạng của nhỏ trong lúc này đi xe bus là không ổn chút nào, Hắn một mực bắt nhỏ lên xe và đưa đến trường. May quá ít ra mặt Hắn cũng có tem đảm bảo nên sau 2 3 lần từ chối, nhỏ cũng chịu lên. Trong lòng tự nhiên thấy vui vui vì làm được việc tốt^^. Hehe. Trên đường đưa nhỏ đi, Hắn bít được nhỏ học Ngân hàng, bình thường thì đi xe máy, mà hum nay mệt nên đi xe bus. Hum nay nhỏ đi học thêm TA, TOEIC j đó ở Chùa Láng, 7h30 là vào lớp, nhà nhỏ ở Định Công… Đưa nhỏ đến lớp xong Hắn và nhỏ tạm biệt, nhỏ cảm ơn và Hắn thì quay xe về nhà. Chuyện tưởng chừng đến đây là có thể kết thúc đc rùi. Nhưng trên đường về, Hắn thấy lạnh, áo cọc, quần ngố mừ, hjx. Nghĩ đến nhỏ bị đau bụng + lạnh thế này, lúc học về chắc cũng 9h 9h30….Sao mình hok làm ng tốt trọn vẹn nhỉ???...Hắn sẽ đón Nhỏ...Ha thế là một kế hoạch được định hình trong đầu, xem nào về nhà cơm nước tắm rửa, mặc thừa 1 cái áo rùi 9h kém ra đợi nhỏ là vừa.. WOAAA, 1 kế hoạch “chuẩn hok cần chỉnh”. Về nhà Hắn tiến hành theo đúng kế hoạch đề ra, hjx có tí ti thay đổi là Hắn mải xem nốt bộ phim nên hơi sai giờ chút^^.Cuối cùng thì cũng ra khỏi nhà,vận trên mình tới 3 cái áo lận…Úi chà chà. Như là sốt rét ế. Nhưng còn lâu mới sốt rét cái trời thế này nhé… Mặc nhìu áo lát về còn cho nhỏ mượn cái áo khoác chứ.:)) Hắn tự thấy mình sao lại chu đáo đến thế (:D.)..Đi đến chỗ nhỏ học là 9h12p. Hie ngồi trà nóng chờ nhỏ chút vậy, chờ đến 9h40p không thấy nhỏ đâu, hắn đoán nhỏ zìa rùi, bùn bùn, vào hỏi một bác thì bác ý bảo : lớp cô ..(j í quên roày) về từ 9h rồi cháu ạ.

Èo. Thế là mất cơ hội làm ng tốt trọn vẹn rùi. Thực sự hắn chẳng hiểu nổi cảm xúc sao lại điều khiển Hắn hành động thế nữa…..Uh thì cứ cho là làm một việc tốt đi…..Hok nghĩ j nữa^^. Hắn trong sáng nhé….:D. Sau ngày hôm nay, có thể trong Hắn cũng hok đọng lại j nhìu về hình ảnh của Nhỏ, về việc mà hắn đã làm. Bởi nó đơn giản như một việc hắn nên làm. Một ngày thu hơi se lạnh + một tâm trạng vui vui khi giúp đc ai đó cũng đủ làm hắn thấy phấn chấn, thấy iu đời.hehe

Có lần hắn đã đọc được:

“Trong cuộc sống, có những điều chắc chắn lại hóa vu vơ

Có những cuộc gặp gỡ bất ngờ lại trở thành định mệnh.” ….Có thể thế hok nhỉ.^^.???