经典面试题:线程中run()方法和start()的区别

结论

话不多说,先上结果:线程直接调用run()方法就相当于一个普通对象调用的他的方法,而只有调用start()方法,线程才会启动,此时才具有抢占CPU资源的资格。当某个线程抢占到 CPU 资源后,会⾃动调⽤ run ⽅法。

分析

这篇博客不仅会讲解标题中的问题,还会说到关于线程的知识,所以我是重头分析的。

1、定义线程的常用的两种方式:

1、继承Thread 2、实现Runable接口。
(1)Thread的源码分析
在这里插入图片描述可见Thread也是继承了Runable接口的,并且有一个run()方法。
在这里插入图片描述
target是什么呢?
在这里插入图片描述
就是一个任务。
(2)Runable接口源码
在这里插入图片描述
很简单,就是一个抽象的run方法。
小结:线程是去抢占 CPU 资源的,任务是具体执⾏业务逻辑的,线程内部会包含⼀个任务,线程启动(start),当抢占到资源之后,任务就开始执⾏(run)。

2、代码演示-自定义线程

1、继承Thread:

package com.demo.Thread;

/**
 * @author wd
 * @version 1.0
 * @date 2020/3/12 23:08
 */
public class MyThread extends Thread {

    @Override
    public void run() {
        for (int i=0; i<10; i++){
            System.out.println("我是李四");
        }
    }
}

2、实现Runable

package com.demo.Thread;

/**
 * @author wd
 * @version 1.0
 * @date 2020/3/12 23:46
 */
public class MyRunable implements Runnable {

    @Override
    public void run() {
        for (int i=0; i<10; i++){
            System.out.println("我是王五");
        }
    }
}

3、写个测试类:
(1)测试两种方式的区别

继承Thread的线程

public class threadTest {
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start();
   }
}

输出结果:
在这里插入图片描述
实现Runable接口

public class threadTest {
    public static void main(String[] args) {
        /*MyThread thread = new MyThread();
        thread.start();*/
        MyRunable task = new MyRunable();
        Thread runThread = new Thread(task);
        runThread.start();
   }
}

输出结果:
在这里插入图片描述

小结

  1. MyThread,继承 Thread 类的⽅式,直接在类中重写 run ⽅法,使⽤的时候,直接实例化MyThread,start 即可,因为 Thread 内部存在 Runnable。
  2. MyRunnbale,实现 Runnable 接⼝的⽅法,在实现类中重写 run ⽅法,使⽤的时候,需要先创建Thread 对象,并将 MyRunnable 注⼊到 Thread 中,Thread.start。
    实际开发中推荐使⽤第⼆种⽅式。因为低耦合,在这里即把线程和任务分开了。

(2)Thread中run方法和start方法的区别
重写测试类:

public class threadTest {
    public static void main(String[] args) {
        MyThread thread1 = new MyThread();
        thread1.run();
        for (int i =0; i<5; i++){
            System.out.println("我是张三");
        }
   }
}

输出结果:
在这里插入图片描述
按照顺序执行的,因为thread1线程压根就没开启,所有操作都是主线程去做的。
把thread1.run();换成thread1.start();重写测试类:

public class threadTest {
    public static void main(String[] args) {
        MyThread thread1 = new MyThread();
        thread1.start();
        for (int i =0; i<10; i++){
            try {
                TimeUnit.MILLISECONDS.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("我是张三");
        }
    }
}

为了有效果,让主线程睡一睡。
输出结果:
在这里插入图片描述
可以看见此时,并不是代码顺序输出的。因为thread1线程开启,会和主线程抢占资源了。也就是有两个线程在跑了。
这也就是run和start两个方法的区别。

奇怪的指数增加了,线程的第三中实现方式

实现 Callable 接⼝
Callable 和 Runnable 的区别在于 Runnable 的 run ⽅法没有返回值,Callable 的 call ⽅法有返回值。
注意:Callable不可以和Runable一样直接用Trhead实现。实现方式如下图:
在这里插入图片描述代码:

public class MyCallableTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyCallable mc = new MyCallable();
        FutureTask futureTask = new FutureTask(mc);
        Thread thread1 = new Thread(futureTask);
        Thread thread2 = new Thread(futureTask);
        thread1.setName("A");
        thread1.start();
        thread2.setName("B");
        thread2.start();
        for (int i = 0;i < 5;i++){
            System.out.println(i);
            System.out.println(futureTask.get());
        }
    }
}

class MyCallable<String> implements Callable<String>{

    @Override
    public String call() throws Exception {
        System.out.println("我是callable要执行的任务");
        return (String) "我是返回的值";
    }
}

在这里插入图片描述
从以上代码的输出结果可得到的一些结论:

  1. 两个Thread都应该输出“我是callable要执行的任务”的,可是只输出了一遍,因为Callable是有缓存的,所以任务只执行一遍。
  2. 代码中有三个线程,最先执行主线程,主线程中要拿futureTask的值,所以他停了,等待Callable线程返回值后再继续向下执行。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章