Vâng rất nhiều câu trả lời hay, tôi muốn thêm nhiều hơn về điều này. Điều này sẽ giúp hiểu Extending v/s Implementing Thread
.
Mở rộng liên kết hai tệp lớp rất chặt chẽ và có thể gây ra một số khá khó khăn để xử lý mã.
Cả hai cách tiếp cận đều làm cùng một công việc nhưng đã có một số khác biệt.
Sự khác biệt phổ biến nhất là
- Khi bạn mở rộng lớp Thread, sau đó bạn không thể mở rộng bất kỳ lớp nào khác mà bạn yêu cầu. (Như bạn đã biết, Java không cho phép kế thừa nhiều hơn một lớp).
- Khi bạn triển khai Runnable, bạn có thể tiết kiệm không gian cho lớp của mình để mở rộng bất kỳ lớp nào khác trong tương lai hoặc ngay bây giờ.
Tuy nhiên, một sự khác biệt đáng kể giữa việc triển khai Runnable và mở rộng Thread là
by extending Thread, each of your threads has a unique object associated with it, whereas implementing Runnable, many threads can share the same object instance.
Ví dụ sau sẽ giúp bạn hiểu rõ hơn
//Implement Runnable Interface...
class ImplementsRunnable implements Runnable {
private int counter = 0;
public void run() {
counter++;
System.out.println("ImplementsRunnable : Counter : " + counter);
}
}
//Extend Thread class...
class ExtendsThread extends Thread {
private int counter = 0;
public void run() {
counter++;
System.out.println("ExtendsThread : Counter : " + counter);
}
}
//Use the above classes here in main to understand the differences more clearly...
public class ThreadVsRunnable {
public static void main(String args[]) throws Exception {
// Multiple threads share the same object.
ImplementsRunnable rc = new ImplementsRunnable();
Thread t1 = new Thread(rc);
t1.start();
Thread.sleep(1000); // Waiting for 1 second before starting next thread
Thread t2 = new Thread(rc);
t2.start();
Thread.sleep(1000); // Waiting for 1 second before starting next thread
Thread t3 = new Thread(rc);
t3.start();
// Creating new instance for every thread access.
ExtendsThread tc1 = new ExtendsThread();
tc1.start();
Thread.sleep(1000); // Waiting for 1 second before starting next thread
ExtendsThread tc2 = new ExtendsThread();
tc2.start();
Thread.sleep(1000); // Waiting for 1 second before starting next thread
ExtendsThread tc3 = new ExtendsThread();
tc3.start();
}
}
Đầu ra của chương trình trên.
ImplementsRunnable : Counter : 1
ImplementsRunnable : Counter : 2
ImplementsRunnable : Counter : 3
ExtendsThread : Counter : 1
ExtendsThread : Counter : 1
ExtendsThread : Counter : 1
Trong cách tiếp cận giao diện Runnable, chỉ có một phiên bản của một lớp được tạo và nó đã được chia sẻ bởi các luồng khác nhau. Vì vậy, giá trị của bộ đếm được tăng lên cho mỗi và mọi truy cập luồng.
Trong khi đó, cách tiếp cận lớp Thread, bạn phải tạo một thể hiện riêng cho mỗi lần truy cập luồng. Do đó bộ nhớ khác nhau được phân bổ cho mỗi phiên bản lớp và mỗi bộ đếm có bộ đếm riêng, giá trị vẫn giữ nguyên, điều đó có nghĩa là không có sự gia tăng nào xảy ra vì không có tham chiếu đối tượng nào giống nhau.
Khi nào nên sử dụng Runnable?
Sử dụng giao diện Runnable khi bạn muốn truy cập cùng một tài nguyên từ nhóm các luồng. Tránh sử dụng lớp Thread ở đây, vì việc tạo nhiều đối tượng tiêu tốn nhiều bộ nhớ hơn và nó trở thành một chi phí hiệu năng lớn.
Một lớp thực hiện Runnable không phải là một chủ đề và chỉ là một lớp. Để Runnable trở thành một Thread, Bạn cần tạo một thể hiện của Thread và tự chuyển nó thành mục tiêu.
Trong hầu hết các trường hợp, giao diện Runnable nên được sử dụng nếu bạn chỉ dự định ghi đè run()
phương thức và không có phương thức Thread nào khác. Điều này rất quan trọng vì các lớp không nên được phân lớp trừ khi lập trình viên có ý định sửa đổi hoặc tăng cường hành vi cơ bản của lớp.
Khi có nhu cầu mở rộng siêu lớp, việc thực hiện giao diện Runnable sẽ phù hợp hơn so với sử dụng lớp Thread. Bởi vì chúng ta có thể mở rộng một lớp khác trong khi thực hiện giao diện Runnable để tạo một luồng.
Hy vọng điều này có thể giúp cho bạn!