Home  >  Q&A  >  body text

Why pass a subclass object of Runnable interface to Thread's constructor?

In addition, compared with thread, in addition to inheritance, how is the independence of code and data reflected in runnable? As written on some blogs, thread cannot share resources, but runnable can share resources. Isn't it enough to change the variables in thread to static? Just like the following article http://blog.csdn.net/uudou/ar...

巴扎黑巴扎黑2661 days ago1518

reply all(3)I'll reply

  • 漂亮男人

    漂亮男人2017-06-12 09:21:22

    It doesn’t seem to have much to do with data. I think Runnable has two benefits:

    1. After implementing Runnable, you can open a thread to run (usually use executorService.exec(command), you can also use new Thread(command).start() if you are a little frustrated), or you can not open the thread blocking method Run (directly call command.run());

    2. Java 1.8 and later can be run with Lambda, for example:

    new Thread(() -> {
        // Do something
    }).start();
    
    
    

    reply
    0
  • 代言

    代言2017-06-12 09:21:22

    The advantage of

    Runnable is that it can be used in various scenarios. For example, you can make any Class implements Runnable, but extends Thread has some limitations. Because of Java single inheritance, it cannot be used in some scenarios. .

    reply
    0
  • 天蓬老师

    天蓬老师2017-06-12 09:21:22

    Answer:

    This problem is considered a design problem.

    The reason why Thread and Runnable are separated is to completely separate the "creation process" of the thread from the "execution logic" of the thread.

    In other words:
    The thread creation process is "code";
    The execution logic of the thread is "data";

    This sounds a bit confusing, isn’t it all JAVA code? Why does code become data again?

    We are not entangled in these concepts. I think we can think about this issue in reverse and give an example to illustrate the problem.

    Discussion process:

    For example, I want to design a single-threaded program. This single-thread needs to complete two tasks:

    1. Print a sentence hello world;
    2. Calculate the sum of the two numbers int a and int b and output it;

    Note: What is execution 1? Or 2? It is determined by the parameter n, n is a random number...

    In order to execute these two tasks in the same thread, we can write code like this:

    float n = (float)Math.random();
    
    int a = 1;
    int b = 1;
    
    Thread t = new Thread() {
        @Override
        public void start() {
            if (n >= 0.5f) {
                System.out.println("hello world");
            } else {
                System.out.println(a + b);
            }
        }
    };
    
    t.start();
    

    The above code can indeed complete the task, but the problem is that we confuse the "creation process" and "business logic" of the thread...

    This is not good. By the way, from an operating system level, the thread creation process is actually very complicated!

    The Java language encapsulates this complexity out of sight. Although the code is just a Thread class and there seems to be no threshold for calling it, the creation process of Thread is still very complicated and consumes resources.

    Back to business, now I add a small requirement again. In addition to the previous 1 and 2, I also add a 3 to display the current timestamp of the system.

    So the task becomes:
    1. Print a sentence hello world;
    2. Calculate the sum of the two numbers int a and int b and output it;
    3. Display the current timestamp of the system;

    Note that at this time we need to modify the creation process of Thread, that is, modify the start function:

    float n = (float)Math.random();
    
    int a = 1;
    int b = 1;
    
    Thread t = new Thread() {
        @Override
        public void start() {
            if (n >= 0.33f) {
                System.out.println("hello world");
            } else if (n >= 0.66f) {
                System.out.println(a + b);
            } else {
                System.out.println(System.currentTimeMillis()); // 这里是新增的语句
            }
        }
    };
    
    t.start();
    

    This discussion ends, let us observe carefully...Actually:

    Thread t = new Thread() {
        @Override
        public void start() {
            // ...
        }
    }
    

    This part of the code remains unchanged, only the code in the start function is modified as the needs change.

    So can we package this part of the changed content into an interface? ?

    This should be a good idea!

    Thread t = new Thread() {
        private Runnable runnable; // 这里定义一个 Runnable 类型的成员
    
        @Override
        public void start() {
            if (null != this.runnable) {
                runnable.run(); // 在这里用接口来把频繁变化的业务逻辑从线程代码里给拿出去,只调用 run 函数
            }
        }
    }
    

    I don’t know if you fully understand it by now? :D

    Haha, doesn’t Java’s Thread class just provide a constructor with Runnable parameters?

    We put the business code into the implementation class of the Runnable interface:

    class BizLogicRun implements Runnable {
        @Override
        public void run() {
            float n = (float)Math.rand();
    
            int a = 1;
            int b = 1;
    
            if (n >= 0.33f) {
                System.out.println("hello world");
            } else if (n >= 0.66f) {
                System.out.println(a + b);
            } else {
                System.out.println(System.currentTimeMillis()); // 这里是新增的语句
            }
        }
    }
    

    So finally, we can call it like this:

    Thread t = new Thread(new BizLogicRun());
    t.start();
    

    This completes the complete separation of the "creation process" and "business logic" of the thread! This "split" also paved the way for Java Thread Pool technology.

    To be honest, Thread t = new Thread() { ... } in the sample code is simple enough, but creating a Thread in the thread pool is not that simple.

    So "split" is very necessary!

    Also, can we imagine:

    class PoolRun implements Runnable {
        List<Runnable> runnableList;
    }
    

    What happens if the Runable implementation class contains a Runnable list?

    Summary:

    1. The purpose of using the Runnable interface is to completely separate the "creation process" of the thread from the "execution logic" of the thread;
    2. Thread cannot share resources, but Runnable can share resources. This statement is incorrect;
    3. In During the discussion, we went from concrete to abstract;
    4. The code I gave in the example is indeed relatively simple, but I hope it can explain the problem;

    Okay, the above is my answer to this question, I hope it will be helpful to you.

    reply
    0
  • Cancelreply