Các vấn đề về thread trong java (phần 1)
Điều phối các thread ngang hàng
HĐH điều phối tiến trình theo phương pháp phân chia cho mỗi tiến trình một khoảng thời gian delta t bằng nhau
Khi một tiến trình bị sleep thì tiến trình khác sẽ chiếm hữu CPU.
Các nguyên tắc khi lập trình thread trong java
Nguyên tắc đầu tiên các bạn nên nhớ đó là không có khái niệm chạy đồng thời. Chúng ta thường nghe nói các thread có thể chạy đồng thời nhưng thực ra đó là cách nói của những vắn tắt của những chuyên gia. Và khi đến tai của chúng ta thì nó đã bị chính các bạn hiểu một cách sai lệch là CPU xử lý đồng thời 2 tiến trình nhưng thực ra trong một thời điểm thì CPU chỉ có thể xử lý được một công việc duy nhất hay nói cách khác là một phép toán duy nhất. Sở dĩ chúng ta có thể thấy 2 kết quả xuất hiện đồng thời khi bạn cho 2 thread chạy với một thời gian sleep như nhau là vì chúng ta không cảm nhận được khoảng thời gian nhường quyền được xử lý giữa 2 thread đó. Giả sử ta có 2 thread ngang hàng A, B. A cho sleep (10000) và B cũng sleep(10000) tức là 10 giây. Sau 10 giây bạn sẽ thấy 2 kết quả xuất hiện đồng thời. Để có thể lý giải được điều này thì mời bạn đọc tiếp. (1)
Thứ 1: Khi hhông sử dụng hàm sleep trong phương thức run của A và B
Các thread sẽ chạy tuần tự. Tức là thread nào khởi tạo trước thì chạy trước, thread nào khởi tạo sau thì chạy sau.
Thứ 2:
Khi sử dụng hàm sleep thì 2 thread A và B sẽ được phân phối dựa theo thời gian sleep của mỗi thread. Thời gian sleep được tính bắt đầu từ lúc bắt đầu khởi chạy và lúc một thread vừa được xử lý xong và bắt đầu chuyển sang trang thái sleep.
Mặc dù hàm sleep(int a) phải truyền vào tham số là một số nguyên dương hoặc 0 nhưng thực ra nếu truyền vào giá trị là 0 thì cũng không có nghĩa là tương đương với việc không sử dụng hàm sleep.
Nên nhớ rằng sleep(0) khác với không sử dụng hàm sleep. Mặc dù đối truyền vào là 0 nhưng thực ra CPU vẫn dành ra một khoảng thời gian > 0 & < 1 (mili giây) để phân phối cho thread đó.
Vì vậy mà nếu A dùng sleep(0) và B dùng sleep(1) thì số lần A được CPU xử lý hiều hơn B và tất nhiên là A sẽ có nhiều kết quả đầu ra hơn B.
Tôi có thể lý giải vấn đề này một cách cụ thể và logic hơn như sau:
+ A sleep(0) nhưng CPU tính là nó sleep trong một khoảng thời gian là delta t với t < 1 mili giây. Tôi giả sử là delta t = 1/3 mili giây.
+ B sleep(1) thì CPU tính là sleep trong 1 mili giây.
Khi bạn cho chạy 2 thread A và B thì CPU sẽ xử lý như sau (giả sử A khởi tạo trước):
A được CPU xử lý trong quá trình đó thì tất cả các thread khác sẽ bị khoá trong vòng 1/3 mili giây. Sau 1/3 mili giây thì B vẫn đang bị khoá nên A tiếp tục được xử lý và sau khi A được xử lý xong thì tổng thời gian sleep của A là 2/3 mili giây sau 3 lần chạy thì B đã ngủ đủ 1 mili giây B bắt đầu chạy. Trong khi đó thì A đang phải sleep tiếp 1/3 mili giây. Sau khi B xử lí xong thì A sẽ tiếp tục được xử lý nhưng nếu A được wakeup trong khi B đang được xử lí thì A sẽ đặt vào trạng thái chờ trong hàng đợi và sau khi B xử lý xong thì ngay lập thức A sẽ được xử lý... Và cứ như vậy ta có thể đưa ra một kết luận là sẽ không bao giờ có chuyện A cho ra nhiều hơn 3 kết quả liên tục và cũng không bao giờ có chuyện B cho ra nhiều hơn 1 kết quả liên tục.
Các bạn có thể kiểm chứng điều này bằng cách A cho sleep(1) và B cho sleep(3).
Bạn sẽ không bao giờ nhận được một kết quả như là:
A
A
A
A
B
hay là:
B
B
A
Nếu cả 2 thread A và B đều có thời gian sleep bằng nhau kể cả là sleep(0) thì các kết quả xử lý sẽ là xen kẽ nhau và bạn sẽ chẳng bao giờ nhận được một kết quả như là:
A
A
B
hoặc là:
B
B
A
mà chỉ có:
A
B
A
B
...
Đối với trường hợp này khi cả A và B sleep đủ thời gian thì cả 2 sẽ được wakeup để xử lý nhưng thread nào khởi tạo trước sẽ được ưu tiên xử lý trước. Thread chưa khi được wakeup mà CPU lại đang bận xử lý thread khác thì nó được đẩy vào hàng đợi. Do đó A, B nối đuôi nhau xử lý. A xử lý xong thì in ngay kết quả ra màn hình trong khi đo B cũng đang được xử lý và in ra kết quả ngay khi nó xử lý xong. Bây giờ bạn đọc lại phần đầu (1) và ngẫm lại thì sẽ hiểu vì sao lại gọi là xử lý đồng thời.
Bây giờ bạn hãy làm một bài tập của tôi để xem bạn hiểu đến đâu nhé:
Hãy cho biết chương trình sau in ra kết quả như thế nào:
public class MyThread {
/**
* @param args
*/
static int i = 0;
static Thread subThread;
public static void main(String[] args) {
subThread = new Thread() {
{
System.out.println("Da khoi tao sub thread");
}
public void run() {
while (i < 100) {
i++;
System.out.println("T1-L2: " + i);
}
}
};
System.out.println("Begin");
new Thread() {
public void run() {
while (i % 2 == 0) {
i++;
System.out.println("T2-L1: " + i);
try {
Thread.sleep(0);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}.start();
new Thread() {
public void run() {
while (i % 2 == 1) {
i++;
// if (!subThread.isAlive())
// subThread.start();
System.out.println("T1-L1: " + i);
try {
Thread.sleep(0);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}.start();
System.out.println("End");
}
}
phương án 1:
T1-L1: 1
T2-L1: 2
phương án 2:
T2-L1: 2
T1-L1: 1
phương án 3:
T1-L1: 1
T2-L1: 2
phương án 4:
Phương án của chính bạn
Hãy suy nghĩ nhưng đừng chạy thử chương trình. Vì đây là kiến thức của bạn.
*sưu tầm*
No comments: