在实现Runnable的类中,要创建线程,我们将实例化Thread类并通过传递Runnable对象来调用声明方法。在这里,为了创建两个线程,我们将创建两个Thread对象以及Runnable对象。但是在扩展Thread类的情况下,我们只为扩展Thread类的类创建两个对象。
class ImplementsRunnable implements Runnable {
private int counter = 0;
public void run() {
counter++;
System.out.println("ImplementsRunnable : Counter : " + counter);
}
}
class ExtendsThread extends Thread {
private int counter = 0;
public void run() {
counter++;
System.out.println("ExtendsThread : Counter : " + counter);
}
}
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();
}
}
答案 0 :(得分:2)
注意:与实例化Thread
实例相比,实例化Runnable
对象的开销更大(在资源方面)。通过扩展Runnable
实现Thread
的思想是线程重用。
从概念上讲,线程对象可以(同步)运行任意数量的任务(这种情况下是可运行的)。例如,执行者可以利用这一点。
Executor executor = Executors.newFixedThreadPool(10);
for(int i = 0; i < 1000; i ++ ) {
executor.execute(() -> System.out.println("test"));
}
在这种情况下,一个10个线程的池运行1000个runanbles。与扩展Thread
相关的开销增加了您必须处理的任务(因此,尽管在您的示例中,差异很小,但如果必须运行10000个任务,差异将变得明显)。
因此,优良作法是实施Runnable
而不是扩展Thread
。
答案 1 :(得分:0)
构造Thread
对象时,您要做的不仅仅是构造该对象。您还可以在JVM中分配线程,该线程通常使用操作系统API进行分配。操作系统线程比进程便宜,但比大多数其他OS对象要重得多,因为每个线程都有自己的执行上下文,需要内核来处理。此外,每个线程都有其自己的执行堆栈,并且必须为此分配空间。在大多数Java实现中,与新线程关联的实际内存分配超过一个兆字节(!)。相比之下,您的Runnable
仅分配几个字节。
如果您在应用程序的整个生命周期中重复使用线程来完成工作,则将分摊内存成本。更重要的是,建立新线程的CPU时间成本非零(通常是syscall,这意味着至少要进行一个上下文切换,并且有时会丢失当前线程的其余部分)。与正在运行的线程通信新工作要比创建新线程少得多。
一个好的经验法则是将java.lang
(Thread
,Object.wait
)中公开的与并发相关的机制视为低级原始操作,并将{{ 1}}(java.util.concurrent
,Executor
等)作为更高级别的操作。低级机制(略)更灵活,但更难正确使用。高层机制同样强大,但是通常可以使您以更高的抽象水平思考问题,这通常会使您的程序更加清晰和正确。