Vòng đời (Các trạng thái của một Thread trong Java? Cách sử dụng các phương thức của lớp Thread?
Trong bài viết này chúng ta cùng tìm hiểu vòng đời (Các trạng thái) của một Thread trong Java và cách sử dụng các phương thức phổ biến của lớp Thread cùng với một số thông tin liên quan đến Thread. Qua bài viết bạn sẽ nắm rõ cách sử dụng Thread trong Java
1. Vòng đời (các trạng thái) của một Thread trong java
- NEW : Đây là trạng thái khi luồng vừa được khởi tạo bằng phương thức khởi tạo của lớp Thread nhưng chưa được start(). Ở trạng thái này, luồng được tạo ra nhưng chưa được cấp phát tài nguyên và cũng chưa chạy. Nếu luồng đang ở trạng thái này mà ta gọi các phương thức ép buộc stop,resume,suspend … sẽ là nguyên nhân sảy ra ngoại lệ IllegalThreadStateException .
- RUNNABLE : Sau khi gọi phương thức start() thì luồng test đã được cấp phát tài nguyên và các lịch điều phối CPU cho luồng test cũng bắt đầu có hiệu lực. Ở đây, chúng ta dùng trạng thái là Runnable chứ không phải Running, vì luồng không thực sự luôn chạy mà tùy vào hệ thống mà có sự điều phối CPU khác nhau.
- WAITING : Thread chờ không giới hạn cho đến khi một luồng khác đánh thức nó.
- TIMED_WAITING : Thread chờ trong một thời gian nhất định, hoặc là có một luồng khác đánh thức nó.
- BLOCKED: Đây là 1 dạng của trạng thái “Not Runnable”, là trạng thái khi Thread vẫn còn sống, nhưng hiện tại không được chọn để chạy. Thread chờ một monitor để unlock một đối tượng mà nó cần.
- TERMINATED : Một thread ở trong trạng thái terminated hoặc dead khi phương thức run() của nó bị thoát.
2. Các phương thức của lớp Thread thường hay sử dụng
- suspend() : Đây là phương thức làm tạm dừng hoạt động của 1 luồng nào đó bằng các ngưng cung cấp CPU cho luồng này. Để cung cấp lại CPU cho luồng ta sử dụng phương thức resume(). Cần lưu ý 1 điều là ta không thể dừng ngay hoạt động của luồng bằng phương thức này. Phương thức suspend() không dừng ngay tức thì hoạt động của luồng mà sau khi luồng này trả CPU về cho hệ điều hành thì không cấp CPU cho luồng nữa.
- resume() : Đây là phương thức làm cho luồng chạy lại khi luồng bị dừng do phương thức suspend() bên trên. Phương thức này sẽ đưa luồng vào lại lịch điều phối CPU để luồng được cấp CPU chạy lại bình thường.
- stop() : Luồng này sẽ kết thúc phương thức run() bằng cách ném ra 1 ngoại lệ ThreadDeath, điều này cũng sẽ làm luồng kết thúc 1 cách ép buộc. Nếu giả sử, trước khi gọi stop() mà luồng đang nắm giữa 1 đối tượng nào đó hoặc 1 tài nguyên nào đó mà luồng khác đang chờ thì có thể dẫn tới việc sảy ra deadlock.
- destroy() : dừng hẳn luồng.
- isAlive() : Phương thức này kiểm tra xem luồng còn active hay không. Phương thức sẽ trả về true nếu luồng đã được start() và chưa rơi vào trạng thái dead. Nếu phương thức trả về false thì luồng đang ở trạng thái “New Thread” hoặc là đang ở trạng thái “Dead”
- yeild() : Hệ điều hành đa nhiệm sẽ phân phối CPU cho các tiến trình, các luồng theo vòng xoay. Mỗi luồng sẽ được cấp CPU trong 1 khoảng thời gian nhất định, sau đó trả lại CPU cho hệ điều hành (HĐH), HĐH sẽ cấp CPU cho luồng khác. Các luồng sẽ nằm chờ trong hàng đợi Ready để nhận CPU theo thứ tự. Java có cung cấp cho chúng ta 1 phương thức khá đặc biệt là yeild(), khi gọi phương thức này luồng sẽ bị ngừng cấp CPU và nhường cho luồng tiếp theo trong hàng chờ Ready. Luồng không phải ngưng cấp CPU như suspend mà chỉ ngưng cấp trong lần nhận CPU đó mà thôi.
- sleep(long) : tạm dừng luồng trong một khoảng thời gian millisecond.
- join() : thông báo rằng hãy chờ thread này hoàn thành rồi thread cha mới được tiếp tục chạy.
- join(long) : Thread cha cần phải đợi millisecond mới được tiếp tục chạy, kể từ lúc gọi join(long). Nếu tham số millis = 0 nghĩa là đợi cho tới khi luồng này kết thúc.
- getName() : Trả về tên của thread.
- setName(String name) : Thay đổi tên của thread.
- getId() : Trả về id của thread.
- getState(): trả về trạng thái của thread.
- currentThread() : Trả về tham chiếu của thread đang được thi hành.
- getPriority() : Trả về mức độ ưu tiên của thread.
- setPriority(int) : Thay đổi mức độ ưu tiên của thread.
- isDaemon() : Kiểm tra nếu thread là một luồng Daemon.
- setDaemon(boolean): xác định thread là một luồng Daemon hay không.
- interrupt() : làm gián đoạn một luồng trong java. Nếu thread nằm trong trạng thái sleep hoặc wait, nghĩa là sleep() hoặc wait() được gọi ra. Việc gọi phương thức interrupt() trên thread đó sẽ phá vỡ trạng thái sleep hoặc wait và ném ra ngoại lệ InterruptedException. Nếu thread không ở trong trạng thái sleep hoặc wait, việc gọi phương thức interrupt() thực hiện hành vi bình thường và không làm gián đoạn thread nhưng đặt cờ interrupt thành true.
- isInterrupted() : kiểm tra nếu thread đã bị ngắt.
- interrupted() : kiểm tra nếu thread hiện tại đã bị ngắt
3. Một số thông tin liên quan đến luồng
Định danh của luồng (ThreadId)
ThreadId là định danh của luồng, nó dùng để phân biệt với các luồng khác cùng tiến trình hoặc cùng tập luồng. Đây là thông số mà máy ảo java tự tạo ra khi ta tạo luồng nên ta không thể sửa đổi cũng như áp đặt thông số này khi tạo luồng. Nhưng ta có thể lấy được nó thông qua phương thức getId() của lớp Thread
Tên của luồng (ThreadName)
ThreadName là tên của luồng, đây là thuộc tính mà ta có thể đặt hoặc không đặt cho luồng. Nếu ta không đặt cho luồng thì máy ảo java sẽ tự đặt với quy tắc sau: “Thread-” + Thứ tự luồng được tạo ra, bắt đầu từ 0.
Độ ưu tiên của luồng (Priority)
Như đã nói ở phần trước, mỗi luồng có 1 độ ưu tiên nhất định. Đây sẽ là thông số quyết định mức ưu tiên khi cấp phát CPU cho các luồng.
Trong java, đế đặt độ ưu tiên cho 1 luồng ta dùng phương thức: void setPriority(int newPriority)
- int newPriority : Là giá trị từ 1 đến 10.
Java có định nghĩa sẵn 3 mức ưu tiên chuẩn như sau:
- Thread.MIN_PRIORITY (giá trị 01)
- hread.NORM_PRIORITY (giá trị 05)
- Thread.MAX_PRIORITY (giá trị 10)
Để lấy độ ưu tiên của 1 luồng, ta dùng phương thức: int getPriority()
Ví dụ minh họa
WorkingThread.java
public class WorkingThread extends Thread {
public WorkingThread(String name) {
super(name);
}
public void run() {
for (int i = 0; i < 5; i++) {
System.out.printf("Luồng: %s có độ ưu tiên là %d \n", getName(), getPriority());
}
}
}
ThreadInfoExample.java
public class ThreadInfoExample {
public static void main(String[] args) {
Thread t1 = new WorkingThread("Luồng 1");
Thread t2 = new WorkingThread("Luồng 2");
Thread t3 = new WorkingThread("Luồng 3");
System.out.println("ID luồng 1: " + t1.getId());
System.out.println("ID luồng 2: " + t2.getId());
System.out.println("ID luồng 3: " + t3.getId());
t1.setPriority(1);
t2.setPriority(5);
t3.setPriority(10);
t1.start();
t2.start();
t3.start();
}
}
Kết quả thực thi chương trình trên:
ID luồng 1: 10
ID luồng 2: 11
ID luồng 3: 12
Luồng: Luồng 2 có độ ưu tiên là 5
Luồng: Luồng 2 có độ ưu tiên là 5
Luồng: Luồng 2 có độ ưu tiên là 5
Luồng: Luồng 2 có độ ưu tiên là 5
Luồng: Luồng 2 có độ ưu tiên là 5
Luồng: Luồng 1 có độ ưu tiên là 1
Luồng: Luồng 3 có độ ưu tiên là 10
Luồng: Luồng 3 có độ ưu tiên là 10
Luồng: Luồng 3 có độ ưu tiên là 10
Luồng: Luồng 3 có độ ưu tiên là 10
Luồng: Luồng 3 có độ ưu tiên là 10
Luồng: Luồng 1 có độ ưu tiên là 1
Luồng: Luồng 1 có độ ưu tiên là 1
Luồng: Luồng 1 có độ ưu tiên là 1
Luồng: Luồng 1 có độ ưu tiên là 1
4. Cách sử dụng các phương thức của lớp Thread và ví dụ minh họa
Sử dụng phương thức sleep()
Phương thức sleep() của lớp Thread được sử dụng để tạm ngừng một thread cho một khoảng thời gian nhất định.
Ví dụ chương trình in ra số từ 1 – 5, tạm ngừng 500 ms trước khi in chữ số tiếp theo.
public class SleepMethodExample extends Thread {
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println(i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String args[]) {
SleepMethodExample t1 = new SleepMethodExample();
t1.start();
}
}
Sử dụng join() và join(long)
join() : thông báo rằng hãy chờ thread này hoàn thành rồi thread cha mới được tiếp tục chạy.
Ví dụ:
public class UsingJoinMethod extends Thread {
public UsingJoinMethod(String name) {
super(name);
}
@Override
public void run() {
System.out.println(getName());
for (int i = 1; i <= 5; i++) {
try {
System.out.print(i + " ");
Thread.sleep(300);
} catch (InterruptedException ie) {
System.out.println(ie.toString());
}
}
System.out.println();
}
public static void main(String[] args) throws InterruptedException {
UsingJoinMethod t1 = new UsingJoinMethod("Thread 1");
UsingJoinMethod t2 = new UsingJoinMethod("Thread 2");
t1.start();
t1.join();
t2.start();
System.out.println("Main Thread Finished");
}
}
Thực thi chương trình trên:
Thread 1
1 2 3 4 5
Main Thread Finished
Thread 2
1 2 3 4 5
join(long) : Thread cha cần phải đợi millisecond mới được tiếp tục chạy, kể từ lúc gọi join(long). Nếu tham số millis = 0 nghĩa là đợi cho tới khi luồng này kết thúc.
public class UsingJoinMethod2 extends Thread {
public UsingJoinMethod2(String name) {
super(name);
}
@Override
public void run() {
System.out.println(getName());
for (int i = 1; i <= 5; i++) {
try {
System.out.print(i + " ");
Thread.sleep(300);
} catch (InterruptedException ie) {
System.out.println(ie.toString());
}
}
System.out.println();
}
public static void main(String[] args) throws InterruptedException {
UsingJoinMethod2 t1 = new UsingJoinMethod2("Thread 1");
UsingJoinMethod2 t2 = new UsingJoinMethod2("Thread 2");
t1.start();
// Main Thread phải chờ 450ms mới được tiếp tục chạy.
// Không nhất thiết phải chờ Thread t1 kết thúc
t1.join(450);
t2.start();
System.out.println("Main Thread Finished");
}
}
Thực thi chương trình trên:
Thread 1
1 2 Main Thread Finished
Thread 2
1 3 2 4 3 5 4
5
Xử lý ngoại lệ cho Thread
Phương thức Thread.setDefaultUncaughtExceptionHandler() thiết lập mặc định xử lý khi luồng đột ngột chấm dứt do một ngoại lệ xảy ra mà không có xử lý khác đã được xác định cho luồng đó.
import java.util.Random;
public class WorkingThread implements Runnable {
@Override
public void run() {
while (true) {
processSomething();
}
}
private void processSomething() {
try {
System.out.println("Processing working thread");
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
Random r = new Random();
int i = r.nextInt(100);
if (i > 70) {
throw new RuntimeException("Simulate an exception was not handled in the thread");
}
}
}
Chương trình minh họa xử lý Thread Exception:
public class ThreadExceptionDemo {
public static void main(String[] args) {
System.out.println("==> Main thread running...");
Thread thread = new Thread(new WorkingThread());
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("#Thread: " + t);
System.out.println("#Thread exception message: " + e.getMessage());
}
});
thread.start();
System.out.println("==> Main thread end!!!");
}
}
Thực thi chương trình trên:
==> Main thread running...
==> Main thread end!!!
Processing working thread
Processing working thread
Processing working thread
Processing working thread
Processing working thread
Processing working thread
#Thread: Thread[Thread-0,5,main]
#Thread exception message: Have a problem...
Theo dõi VnCoder trên Facebook, để cập nhật những bài viết, tin tức và khoá học mới nhất!