通过实现Runnable接口如何减少内存消耗?

时间:2018-09-27 05:18:15

标签: java multithreading

在实现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();
 }
 }

2 个答案:

答案 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.langThreadObject.wait)中公开的与并发相关的机制视为低级原始操作,并将{{ 1}}(java.util.concurrentExecutor等)作为更高级别的操作。低级机制(略)更灵活,但更难正确使用。高层机制同样强大,但是通常可以使您以更高的抽象水平思考问题,这通常会使您的程序更加清晰和正确。