Thread (Luồng) trong Java
- 14-11-2023
- Toanngo92
- 0 Comments
Mục lục
Giới Thiệu
Một tiến trình là một chương trình đang thực thi. Mỗi tiến trình có các tài nguyên thời gian chạy riêng biệt của mình, như dữ liệu riêng, biến và không gian bộ nhớ riêng. Các tài nguyên thời gian chạy này tạo nên một môi trường thực thi cho các tiến trình bên trong một chương trình. Do đó, mỗi tiến trình có một môi trường thực thi tự chủ để chạy độc lập. Mỗi tiến trình thực hiện nhiều công việc cùng một lúc và mỗi công việc được thực hiện bởi các luồng riêng biệt. Một luồng không gì khác ngoài, đơn vị cơ bản mà hệ điều hành cấp phát thời gian xử lý. Một luồng là thực thể trong một tiến trình có thể được lên lịch để thực thi. Một tiến trình bắt đầu với một luồng duy nhất, thường được gọi là luồng chính, mặc định hoặc chính. Vì vậy, một tiến trình lại chứa một hoặc nhiều luồng.
Một luồng có các đặc điểm sau:
Một luồng có bộ tài nguyên thời gian chạy cơ bản đầy đủ của mình để chạy độc lập.
Một luồng là đơn vị nhỏ nhất của mã thực thi trong ứng dụng thực hiện một công việc cụ thể.
Nhiều luồng có thể được thực thi cùng một lúc, tạo điều kiện thực hiện nhiều công việc của một ứng dụng duy nhất đồng thời.
So Sánh Giữa Tiến Trình và Luồng
Nhiều luồng cho phép truy cập vào cùng một phần bộ nhớ, trong khi tiến trình không thể truy cập trực tiếp vào bộ nhớ của một tiến trình khác.
Một số sự tương đồng và khác biệt giữa tiến trình và luồng như sau:
Tương Đồng
- Các luồng chia sẻ một đơn vị xử lý trung ương và chỉ có một luồng hoạt động (đang chạy) vào một thời điểm.
- Các luồng bên trong tiến trình thực hiện theo thứ tự.
- Một luồng có thể tạo ra các luồng con hoặc luồng phụ.
- Nếu một luồng bị chặn, một luồng khác có thể chạy.
Khác Biệt
- Khác với tiến trình, các luồng không độc lập lẫn nhau.
- Khác với tiến trình, tất cả các luồng có thể truy cập mọi địa chỉ trong nhiệm vụ.
Ứng Dụng và Sử Dụng của Luồng
Trong Java, một Luồng hỗ trợ xử lý song song của nhiều nhiệm vụ.
Một số ứng dụng của luồng như sau:
Chạy âm thanh và hiển thị hình ảnh đồng thời.
Tạo Threads
Một cách dễ dàng để tạo một luồng mới là kế thừa một lớp từ lớp java.lang.Thread. Lớp này bao gồm các phương thức và constructors, giúp thực hiện khái niệm lập trình đồng thời trong Java. Điều này được sử dụng để tạo các ứng dụng có thể thực hiện nhiều nhiệm vụ đồng thời.
Quy trình từng bước để tạo một luồng mới bằng cách mở rộng lớp Thread được thảo luận ở đây.
Bước 1: Tạo Một Lớp Con
Khai báo một lớp là lớp con của lớp Thread được định nghĩa trong gói java.lang.
Đoạn mã 1 dưới đây thể hiện việc tạo lớp con. Lớp MyThread sẽ kế thừa các khả năng của lớp Thread và cho phép thực hiện các nhiệm vụ đa luồng.
class MyThread extends Thread {
// Kế thừa từ lớp Thread
// Định nghĩa lớp
}
Bước 2: Ghi đè phương thức run()
Bên trong lớp con, ghi đè phương thức run() được định nghĩa trong lớp Thread. Đoạn mã trong phương thức run() xác định chức năng cần thiết để luồng thực thi. Phương thức run() trong một luồng tương tự như phương thức main() trong một ứng dụng.
Đoạn mã 2 hiển thị cách triển khai của phương thức run().
class MyThread extends Thread { // Kế thừa từ lớp Thread
// Định nghĩa lớp
public void run() { // Ghi đè phương thức run()
// Triển khai
}
}
Bước 3: Khởi động luồng
Phương thức main() tạo một đối tượng của lớp kế thừa từ lớp Thread. Sau đó, phương thức start() được gọi trên đối tượng để bắt đầu luồng. Phương thức start() sẽ đặt đối tượng Luồng vào trạng thái chạy. Phương thức start() của một luồng gọi phương thức run(), nơi cung cấp các tài nguyên cần thiết để chạy luồng.
public static void main(String[] args) {
// Tạo đối tượng của lớp con
MyThread myThread = new MyThread();
// Khởi động luồng bằng cách gọi start()
myThread.start();
}
Hàm khởi tạo và các phương thức lớp Thread
Bảng dưới mô tả Constructor lớp Thread
Constructor | Mô tả |
---|---|
Thread() | Constructor mặc định |
Thread(Runnable objRun) | Tạo một đối tượng Thread mới, trong đó objRun là đối tượng, phương thức run() của nó được gọi |
Thread(Runnable objRun, String threadName) | Tạo một đối tượng Thread mới có tên, trong đó objRun là đối tượng, phương thức run() của nó được gọi và threadName là tên của luồng sẽ được tạo |
Thread(String threadName) | Tạo một đối tượng Thread mới với threadName là tên của luồng sẽ được tạo |
Thread(ThreadGroup group, Runnable objRun) | Tạo một đối tượng Thread mới, trong đó group là nhóm luồng và objRun là đối tượng, phương thức run() của nó được gọi |
Thread(ThreadGroup group, Runnable objRun, String threadName) | Tạo một đối tượng Thread mới có tên, trong đó group là nhóm luồng, objRun là đối tượng, phương thức run() của nó được gọi và threadName là tên của luồng sẽ được tạo |
Thread(ThreadGroup group, Runnable objRun, String threadName, long stackSize) | Tạo một đối tượng Thread mới có tên, trong đó group là nhóm luồng, objRun là đối tượng, phương thức run() của nó được gọi, threadName là tên của luồng sẽ được tạo và stackSize là kích thước của ngăn xếp cho luồng |
Lớp Thread cung cấp một số phương thức để làm việc với chương trình đa luồng, mô tả ở bảng dưới:
Method | Mô tả |
---|---|
static int activeCount() | Trả về số lượng luồng đang hoạt động trong số luồng hiện tại trong chương trình |
static Thread currentThread() | Trả về tham chiếu đến luồng đang thực thi hiện tại |
ThreadGroup getThreadGroup() | Trả về nhóm luồng mà luồng này thuộc về |
static boolean interrupted() | Kiểm tra xem luồng hiện tại đã bị gián đoạn hay chưa |
boolean isAlive() | Kiểm tra xem luồng này còn sống hay không |
boolean isInterrupted() | Kiểm tra xem luồng này đã bị gián đoạn hay chưa |
void join() | Đợi cho đến khi luồng này kết thúc |
void setName(String name) | Đặt tên mới cho luồng này để bằng với đối số name |
Ví dụ:
package demo;
// NamedThread is created as a subclass of the class Thread
public class NamedThread extends Thread {
String name;
// This method of Thread class is overridden to specify the action that will be done
// when the thread begins execution
public void run() {
// Will store the number of threads
int count = 0;
while (count < 4) {
// Display the number of threads
System.out.println(Thread.activeCount());
// Display the name of the currently running thread
name = Thread.currentThread().getName();
count++;
System.out.println(name);
if (name.equals("Thread-0")) {
System.out.println("Marimba");
} else {
System.out.println("Jini");
}
try {
// Sleep for a short duration to allow other threads to run
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
NamedThread objNamedThread = new NamedThread();
objNamedThread.setName("Thread-0");
// Display the status of the thread, whether alive or not
System.out.println(Thread.currentThread().isAlive());
System.out.println(objNamedThread.isAlive());
// Invokes the start method which in turn will call
// run and begin thread execution
objNamedThread.start();
// Display the status of the thread after it has started
System.out.println(Thread.currentThread().isAlive());
System.out.println(objNamedThread.isAlive());
}
}
Lưu ý: Lập trình đồng thời (Concurrent Programming)
Lập trình đồng thời là quá trình chạy nhiều nhiệm vụ cùng một lúc.
Trong Java, có thể thực hiện đồng thời một phương thức được gọi và các câu lệnh sau lời gọi phương thức mà không cần đợi phương thức được gọi kết thúc.
Phương thức được gọi chạy độc lập và đồng thời với chương trình gọi, và có thể chia sẻ biến, dữ liệu và các thành phần khác với nó.
Giao diện Runnable
Giao diện Runnable
được thiết kế để cung cấp một tập hợp các quy tắc chung cho các đối tượng muốn thực hiện mã khi một luồng đang hoạt động. Một cách khác để tạo một luồng mới là bằng cách triển khai giao diện Runnable
. Phương pháp này có thể được sử dụng vì Java không cho phép đa kế thừa lớp. Do đó, tùy thuộc vào yêu cầu cụ thể, cả hai phương pháp có thể được sử dụng để tạo luồng trong Java.
Bước 1: Triển khai giao diện Runnable
Khai báo một lớp triển khai giao diện Runnable
. Đoạn mã Snippet 5 triển khai giao diện Runnable
.
// Khai báo một lớp triển khai giao diện Runnable
class MyRunnable implements Runnable {
}
Bước 2: Triển khai phương thức run()
Giao diện Runnable
định nghĩa một phương thức run()
, chứa mã sẽ được thực thi bởi đối tượng luồng. Lớp triển khai giao diện Runnable
nên ghi đè phương thức run()
. Đoạn mã Snippet 6 triển khai phương thức run()
.
// Khai báo một lớp triển khai giao diện Runnable
class MyRunnable implements Runnable {
// Ghi đè phương thức run()
public void run() {
// Thực hiện công việc của luồng
}
}
Bước 3: Bắt đầu luồng
Trong phương thức main()
, tạo một đối tượng của lớp triển khai giao diện Runnable
. Sau đó, chuyển đối tượng này vào hàm tạo của lớp Thread
để tạo một đối tượng luồng. Cuối cùng, gọi phương thức start()
trên đối tượng luồng để bắt đầu luồng.
// Phương thức main
public static void main(String[] args) {
// Tạo đối tượng của lớp triển khai Runnable
MyRunnable myRunnable = new MyRunnable();
// Chuyển đối tượng vào hàm tạo của lớp Thread
Thread myThread = new Thread(myRunnable);
// Gọi phương thức start() để bắt đầu luồng
myThread.start();
}
Ví dụ triển khai hoàn thiện:
Tạo lớp NamedThread
package test;
// NamedThread is created to implement the interface Runnable
class NamedThread implements Runnable {
// This will store the name of the thread
String name;
// The method of Runnable is implemented to specify the action
// that will be done when the thread begins execution.
public void run() {
int count = 0; // Will store the number of threads
while (count < 3) {
name = Thread.currentThread().getName();
System.out.println(name);
count++;
}
}
}
Tạo lớp MainTest
public class MainTest {
public static void main(String[] args) {
NamedThread objNewThread = new NamedThread();
Thread newThread = new Thread(objNewThread);
newThread.start();
}
}
Output:
Thread-0
Thread-0
Thread-0
Vòng đời của Thread
Trạng thái luồng (Thread States)
Một luồng có thể tồn tại trong nhiều trạng thái khác nhau, chẳng hạn như new (mới), runnable (chạy được), blocked (bị chặn), waiting (đang đợi), và terminated (kết thúc), tùy thuộc vào các giai đoạn khác nhau trong chương trình. Khi một luồng mới được tạo, nó ở trạng thái mới (new thread) và chưa được kích hoạt (alive).
Trong trạng thái này, đó là một đối tượng Luồng mới không chứa bất kỳ nguồn tài nguyên hệ thống nào đã được phân bổ. Do đó, luồng ở trạng thái “luồng mới” cho đến khi phương thức start()
được gọi trên nó. Khi một luồng ở trong trạng thái này, bạn chỉ có thể bắt đầu hoặc dừng luồng đó. Gọi bất kỳ phương thức nào khác trước khi bắt đầu luồng sẽ gây ra IllegalThreadStateException
.
Hình dưới mô tả trạng thái new của luồng
Trạng thái này xảy ra khi biến thể hiện Thread vừa được tạo:
Thread thObj = new Thread();
Trạng thái chạy được (Runnable State)
Một luồng mới có thể ở trạng thái chạy được khi phương thức start()
được gọi trên nó. Một luồng ở trạng thái này là đang chạy. Luồng có thể chuyển sang trạng thái chạy được từ trạng thái đang chạy hoặc bị chặn.
Các luồng được ưu tiên vì trong một hệ thống có một bộ xử lý duy nhất, không thể thực hiện tất cả các luồng chạy được cùng một lúc. Trong trạng thái chạy được, một luồng có đủ điều kiện để chạy, nhưng có thể không chạy ngay lập tức tùy thuộc vào ưu tiên của luồng đó. Khi một luồng chạy được trở nên đủ điều kiện để chạy, nó thực thi các chỉ thị trong phương thức run()
của nó.
Hình dưới mô tả trạng thái Runnable
Trạng thái này xảy ra khi biến thể hiện Thread gọi phương thức start():
Thread thObj = new Thread();
thObj.start();
Lưu ý: Trạng thái này được gọi là chạy được vì mặc dù luồng có thể không chạy, nhưng luồng đã được cấp phát tất cả các tài nguyên để chạy.
Lập lịch là một thành phần trong Java gán ưu tiên cho các luồng để thực hiện theo yêu cầu.
Trạng thái bị chặn (Blocked State)
Blocked State
(Trạng thái bị chặn) là một trong những trạng thái mà một luồng:
- Đang sống nhưng hiện không thể chạy vì đã bị chặn do một số hoạt động khác.
- Không chạy được nhưng có thể quay lại trạng thái chạy được sau khi có được giám sát hoặc khóa.
Một luồng trong trạng thái bị chặn đang đợi để thực hiện các hoạt động trên tài nguyên hoặc đối tượng đang được xử lý bởi một luồng khác. Một luồng đang chạy chuyển sang trạng thái bị chặn khi phương thức sleep()
, wait()
, hoặc suspend()
được gọi trên nó.
Ghi chú: Khóa hoặc giám sát là một hộp ảo chứa một đối tượng. Hộp ảo này chỉ cho phép một luồng duy nhất vào để nó có thể thao tác trên đối tượng. Bất kỳ luồng nào muốn chia sẻ nguồn lực hoặc đối tượng phải nhận khóa.
Trạng thái Chờ
Một luồng ở trong trạng thái này khi nó đang chờ đợi một luồng khác giải phóng nguồn lực cho nó. Khi hai hoặc nhiều luồng chạy song song và chỉ có một luồng giữ nguồn lực suốt thời gian, các luồng khác cuối cùng sẽ đợi luồng này giải phóng nguồn lực cho chúng. Trong trạng thái này, một luồng vẫn còn sống nhưng không chạy.
Một cuộc gọi đến phương thức wait() đặt một luồng vào trạng thái chờ đợi này. Gọi phương thức notify() hoặc notifyAll() đưa luồng từ trạng thái chờ đợi đến trạng thái có thể chạy được.
Hình dưới mô tả trạng thái chờ
Chú ý: Nếu phương thức start() được gọi trên một trạng thái đã kết thúc, sẽ ném một ngoại lệ thời gian chạy.
Các phương thức lớp Thread
Lớp Thread
trong Java bao gồm nhiều phương thức có thể được sử dụng để thao tác với các luồng.
Tất cả các luồng đều có một tên được liên kết với chúng. Đôi khi, cần phải lấy tên của một luồng cụ thể. Phương thức getName()
giúp lấy tên của luồng hiện tại.
Ví dụ:
public class ThreadGetNameExample {
public static void main(String[] args) {
// Creating two threads
Thread thread1 = new MyThread("Thread 1");
Thread thread2 = new MyThread("Thread 2");
// Start the threads
thread1.start();
thread2.start();
}
}
class MyThread extends Thread {
public MyThread(String name) {
super(name);
}
@Override
public void run() {
// Display the name of the current thread
System.out.println("Thread Name: " + Thread.currentThread().getName());
}
}
Output:
Thread Name: Thread 1
Thread Name: Thread 2
Ghi chú: Phương thức
setName()
gán một tên cho một đối tượng luồng. Tên được truyền như một đối số cho phương thức.public final void setName(String name)
Ở đây,
name
là một đối số kiểu String được truyền vào phương thứcsetName()
.
Phương thức start()
Một luồng mới được tạo sẽ ở trạng thái đợi đến khi phương thức start() được gọi. Phương thức start() cấp phát các tài nguyên hệ thống cần thiết để chạy luồng và thực thi phương thức run() của đối tượng mục tiêu của nó.
Khi gọi phương thức start(), các hành động sau xảy ra:
- Một luồng thực thi mới được khởi động.
- Luồng chuyển từ trạng thái luồng mới sang trạng thái có thể chạy (runnable).
Ví dụ:
Thread thObj = new Thread();
thObj.start();
Phương thức run()
Cuộc sống của một luồng bắt đầu khi phương thức run() được gọi. Các đặc điểm của phương thức run() như sau:
- Nó là một phương thức công cộng (
public
). - Không chấp nhận đối số nào.
- Không trả về bất kỳ giá trị nào.
- Không ném bất kỳ ngoại lệ nào.
Phương thức run() chứa các chỉ thị, sẽ được thực thi khi phương thức start() được gọi.
Ví dụ:
// Khai báo một lớp triển khai giao diện Runnable
class MyRunnable implements Runnable {
// Ghi đè phương thức run()
public void run() {
// Thực hiện công việc của luồng
}
}
Phương thức sleep()
Phương thức sleep() có các đặc điểm sau:
- Nó tạm dừng việc thực thi của luồng hiện tại trong một khoảng thời gian nhất định.
- Nó làm cho thời gian xử lý sẵn có để các luồng khác của ứng dụng hoặc các ứng dụng khác có thể chạy trên hệ thống máy tính.
- Nó dừng thực thi nếu luồng hiện tại trong khoảng thời gian được chỉ định tính bằng mili giây hoặc nanogian.
- Nó ném ra một ngoại lệ
InterruptedException
khi bị gián đoạn bằng cách sử dụng phương thứcinterrupt()
.
Cú pháp:
void sleep(long mills);
Ví dụ:
public class SleepMethodExample {
public static void main(String[] args) {
for (int i = 1; i <= 5; i++) {
System.out.println("Iteration " + i);
try {
// Sleep for 1 second (1000 milliseconds)
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Main thread finished.");
}
}
Phương thức interrupt()
Phương thức interrupt() gián đoạn luồng. Phương thức này yêu cầu luồng dừng lại ngay cả trước khi hoàn thành nhiệm vụ. Phương thức interrupt() có các đặc điểm sau.
- Một luồng bị gián đoạn có thể chấm dứt, đợi cho một nhiệm vụ khác hoặc tiếp tục sang bước tiếp theo tùy thuộc vào yêu cầu của ứng dụng.
- Nó không gián đoạn hoặc dừng một luồng đang chạy; thay vào đó, nó ném một
InterruptedException
nếu luồng bị chặn, để nó thoát khỏi trạng thái chặn, - Nếu luồng bị chặn bởi các phương thức wait(), join() hoặc sleep(), nó nhận một Interrupted=xception, do đó kết thúc phương thức chặn trước thời gian dự kiến.
Cú pháp:
public void interrupt()
Lưu ý: Một luồng có thể tự gián đoạn chính nó. Khi một luồng không tự gián đoạn, phương thức checkAccess() được gọi. Phương thức này kiểm tra xem luồng hiện tại có đủ quyền để sửa đổi luồng hay không. Trong trường hợp quyền không đủ, bảo vệ an toàn ném ra một SecurityException để chỉ ra vi phạm an toàn.
Ví dụ:
public class InterruptExample {
public static void main(String[] args) {
Thread myThread = new MyThread();
myThread.start();
// Main thread sleeps for 3 seconds
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// Interrupt the MyThread
myThread.interrupt();
}
}
class MyThread extends Thread {
@Override
public void run() {
try {
// Thread sleeps for a long time (simulating a blocking operation)
while (true) {
System.out.println("Thread is running...");
Thread.sleep(1000);
}
} catch (InterruptedException e) {
// Handle the InterruptedException
System.out.println("Thread interrupted! Exiting...");
}
}
}
Quản lý luồng
Trong các hoạt động hàng ngày của chúng ta, cần phải gán các mức độ quan trọng khác nhau cho các nhiệm vụ khác nhau. Quyết định được đưa ra để đạt được kết quả tốt nhất có thể. Ví dụ, khi quyết định giữa việc đi làm và xem truyền hình, việc đi làm được ưu tiên hơn so với việc xem truyền hình.
Điều này bởi vì việc đi làm quan trọng hơn đối với lợi ích tài chính của người đó so với việc giải trí đơn thuần. Tương tự, trong lập trình Java, việc ưu tiên các luồng theo mức quan trọng là cần thiết.
Luồng là các thực thể tự thực thi bên trong một chương trình. Trong một chương trình duy nhất, có thể có nhiều luồng thực thi độc lập lẫn nhau. Tuy nhiên, đôi khi có thể xảy ra tình huống mà một tài nguyên thực thi cụ thể phải được chia sẻ bởi nhiều luồng đang chạy đồng thời. Điều này buộc các luồng khác đang chạy phải chuyển sang trạng thái chặn. Do đó, trong những tình huống như vậy, cần có một sự kiểm soát hoặc quản lý nội bộ của các luồng để chúng được thực thi đồng thời.
Hình dưới mô tả chương trình thực thi đa luồng
Lưu ý: Ví dụ về một trình xử lý từ vựng; có nhiều luồng đang chạy cùng một lúc, như các luồng để chấp nhận từ vựng được gõ, tự động lưu và kiểm tra chính tả. Do đó, cần quản lý tài nguyên hệ thống một cách hiệu quả để chạy tất cả các nhiệm vụ mà không có xung đột. Khi ứng dụng được đóng, tất cả các luồng nên được chấm dứt đồng thời.
Sự Cần Thiết của Ưu Tiên Luồng
Trong khi tạo ứng dụng đa luồng, có thể xảy ra tình huống một luồng đã chạy và bạn muốn chạy một luồng khác quan trọng hơn. Đây là nơi ưu tiên luồng đóng một vai trò quan trọng. Ưu tiên được sử dụng để diễn đạt sự quan trọng của các luồng khác nhau. Ưu tiên đóng một vai trò quan trọng khi có sự tranh chấp nặng nề giữa các luồng cố gắng có cơ hội thực thi. Quá trình ưu tiên này được quản lý bởi trình lên lịch mà gán ưu tiên cho các luồng tương ứng.
Lưu ý: Ưu tiên luồng tương tự như cuộc sống hàng ngày của chúng ta, nơi chúng ta phải ưu tiên lịch trình hoặc quyết định của mình dựa trên một số yếu tố để mọi công việc được thực hiện theo đúng ưu tiên của chúng.
Các Loại Ưu Tiên Luồng
Ưu tiên luồng giúp lên lịch trình luồng quyết định luồng nào được chạy. Ưu tiên cũng giúp hệ điều hành quyết định lượng tài nguyên cần được phân bổ cho mỗi luồng. Ưu tiên luồng là số nguyên từ Thread.MIN_PRIORITY đến Thread.MAX_PRIORITY. Số càng cao, ưu tiên càng cao. Luồng có ưu tiên cao hơn sẽ nhận được nhiều thời gian CPU hơn so với luồng có ưu tiên thấp hơn. Ưu tiên luồng trong Java là các hằng số được định nghĩa trong lớp Thread:
- Thread.MAX_PRIORITY: Giá trị hằng số là 10, có độ ưu tiên cao nhất.
- Thread.NORM_PRIORITY: Giá trị hằng số là 5, đây là độ ưu tiên mặc định.
- Thread.MIN_PRIORITY: Giá trị hằng số là 1, có độ ưu tiên thấp nhất.
Lưu ý: Mọi luồng có độ ưu tiên mặc định là NORM_PRIORITY.
Phương thức setPriority()
Một luồng mới được tạo sẽ kế thừa độ ưu tiên từ luồng đã tạo nó. Để thay đổi độ ưu tiên của một luồng, ta sử dụng phương thức setPriority(). Phương thức setPriority() thay đổi độ ưu tiên hiện tại của một luồng. Phương thức setPriority() chấp nhận giá trị nguyên từ 1 đến 10.
Cú pháp:
public final void setPriority(int newPriority)
Ví dụ:
Tạo lớp MyThread
class MyThread extends Thread {
public MyThread(String name) {
super(name);
}
@Override
public void run() {
System.out.println("Thread " + getName() + " is running.");
}
}
Tạo lớp ThreadPriorityExample
public class ThreadPriorityExample {
public static void main(String[] args) {
Thread thread1 = new MyThread("Thread 1");
Thread thread2 = new MyThread("Thread 2");
// Display the default priorities of the threads
System.out.println("Thread 1 Default Priority: " + thread1.getPriority());
System.out.println("Thread 2 Default Priority: " + thread2.getPriority());
// Set new priorities for the threads
thread1.setPriority(Thread.MIN_PRIORITY);
thread2.setPriority(Thread.MAX_PRIORITY);
// Start the threads
thread1.start();
thread2.start();
// Display the updated priorities of the threads
System.out.println("Thread 1 Updated Priority: " + thread1.getPriority());
System.out.println("Thread 2 Updated Priority: " + thread2.getPriority());
}
}
Output:
Thread 1 Default Priority: 5
Thread 2 Default Priority: 5
Thread 1 Updated Priority: 1
Thread 2 Updated Priority: 10
Phương thức getPriority()
Phương thức getPriority() giúp lấy giá trị độ ưu tiên hiện tại của một luồng. Điều này giúp truy vấn để biết giá trị độ ưu tiên hiện tại của luồng đang chạy để đảm bảo rằng luồng đang chạy ở mức độ ưu tiên yêu cầu.
Cú pháp:
public final int getPriority()
Ví dụ:
public class ThreadPriorityExample {
public static void main(String[] args) {
Thread thread1 = new MyThread("Thread 1");
Thread thread2 = new MyThread("Thread 2");
// Start the threads
thread1.start();
thread2.start();
// Display the priorities of the threads
System.out.println("Thread 1 Priority: " + thread1.getPriority());
System.out.println("Thread 2 Priority: " + thread2.getPriority());
}
}
class MyThread extends Thread {
public MyThread(String name) {
super(name);
}
@Override
public void run() {
System.out.println("Thread " + getName() + " is running.");
}
}
Output:
Thread 1 is running.
Thread 2 is running.
Thread 1 Priority: 5
Thread 2 Priority: 5
Luồng Daemon
Một luồng daemon chạy liên tục để thực hiện một dịch vụ, mà không có bất kỳ kết nối nào với trạng thái tổng thể của chương trình. Nói chung, các luồng chạy mã hệ thống là ví dụ tốt về luồng daemon.
Các đặc điểm của các luồng daemon như sau:
- Chúng hoạt động ở nền, cung cấp dịch vụ cho các luồng khác.
- Chúng hoàn toàn phụ thuộc vào các luồng người dùng.
Lúc một luồng chết, máy ảo Java sẽ dừng lại và chỉ có luồng daemon là còn sống.
Lớp Thread có hai phương thức liên quan đến các luồng daemon:
setDaemon(boolean value): Phương thức này chuyển đổi một luồng người dùng thành luồng daemon. Nó nhận một giá trị boolean làm đối số. Để đặt một luồng làm luồng daemon, phương thức setDaemon() được gọi với giá trị true. Mặc định, mỗi luồng là luồng người dùng trừ khi nó được đặt là luồng daemon trước đó.
Cú pháp:
void setDaemon(boolean val)
isDaemon(): Phương thức này xác định xem một luồng có phải là luồng daemon hay không. Nó trả về true
nếu luồng này là luồng daemon, ngược lại trả về false.
Cú pháp:
boolean isDaemon()
Chú ý: Ví dụ về các luồng daemon bao gồm luồng thu gom rác và luồng xử lý sự kiện chuột cho một chương trình Java. Ngược lại, các luồng người dùng là các luồng mặc định quan trọng đối với việc thực thi của chương trình.
Ví dụ:
public class DaemonThreadExample {
public static void main(String[] args) {
// Creating a daemon thread
Thread daemonThread = new MyDaemonThread();
// Setting the thread as daemon
daemonThread.setDaemon(true);
// Start the daemon thread
daemonThread.start();
// Main thread (non-daemon) continues its execution
for (int i = 0; i < 5; i++) {
System.out.println("Main thread is running...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Main thread finished.");
}
}
class MyDaemonThread extends Thread {
@Override
public void run() {
// Daemon thread runs in the background
while (true) {
System.out.println("Daemon thread is running...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Sự Cần Thiết của Các Luồng Daemon
Các nhiệm vụ thực hiện bởi các luồng Daemon bao gồm:
- Các luồng Daemon là nhà cung cấp dịch vụ cho các luồng khác đang chạy trong cùng một quy trình.
- Các luồng Daemon được thiết kế như các luồng nền cấp thấp thực hiện một số công việc như sự kiện chuột cho chương trình Java.
- Chúng thường được sử dụng cho các tác vụ nền, chẳng hạn như thu thập và giám sát rác.
Chú ý: Một luồng có thể được đặt là Daemon khi lập trình viên không muốn chương trình chính phải đợi cho đến khi một luồng kết thúc.