【Java多线程】带你用不一样的思维看创建线程的两种方式

创建线程的两种方式

通过实现 Runnable 接口

public class CreateThread {
//  继承 Thread 类来
    static class MyThread extends Thread{
        @Override
        public void run() {
            System.out.println("Hello MyThread");
        }
    }

//  实现 Runnable 接口:这个方式更灵活,因为实现接口后,可以继承其他类
    static class MyRun implements Runnable{
        @Override
        public void run() {
            System.out.println("Hello MyRun");
        }
    }



    public static void main(String[] args) {
        new MyThread().start();
        new Thread(new MyRun()).start();
        new Thread(()->{
            System.out.println("hello lambda!");
        }).start();
    }
}

上面是主流的创建线程的两种方法,那么我们看一下这两种方法有什么区别吧
Runnable:可以看到使用 Runnable 来创建线程,使用 Runnable 接口引向一个匿名内部类,该内部类重写了 Runnable 接口的run方法,再次之后,仍需要创建一个新的线程,然后传入 runnable ;
Thread : 直接继承 Thread 然后重写 run 方法

从面对对象的角度来思考:
两种方法都是为了构造一个 Thread 类,大家可以看到实现了 runnable 接口后,仍然是创建了一个新的线程,把接口作为 Thread 的构造方法参数传入

public Thread(Runnable target) {
    init(null, target, "Thread-" + nextThreadNum(), 0);
}

start 和 run 方法解析

在初次使用的时候,大家很容易看见,我们重写的是 run 方法,那么为什么调用的时候,调用的却是 start 方法

start启动新线程:通知JVM,有空闲就创建新线程,何时创建由线程调度器控制(并非调用start方法就会创建,即两个调用start的方法顺序不代表线程的创建顺序)

调用完start后,在主线程中启动

    public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }

为什么不能调用 run 方法 调用了会发生什么事情

public static void main(String[] args) {
    Runnable runnable = ()->{
        System.out.println(Thread.currentThread().getName());
    };
    runnable.run();

    new Thread(runnable).start();
}

main:因为在调用的时候,我们使用的是接口.方法,实际上 runnable 接口指向一个匿名内部类,这个类重写了 run 方法,我们在调用的时候,调用的是普通方法 run ,并且是在主函数里面调用的 主线程就是 main
Thread-0 :

重复调用 start 方法会发生什么事情

Exception in thread “main” java.lang.IllegalThreadStateException
at java.lang.Thread.start(Thread.java:708)
at StartTwice.main(StartTwice.java:5)

为什么不能重复调用
原因:一旦开始线程完毕,会进入终止状态,无法回退

if (threadStatus != 0)
 throw new IllegalThreadStateException();

threadStatus:
Java 线程状态,已初始化以指示线程“尚未启动”

彩蛋:同时用两种方法来实现同一个线程,会遇到什么

大家可能没有考虑过,如果new Thread 类,并且传入一个 runnable 重写 runnable 接口的 run 方法的同时,重写Thread 类的 run 方法会发生什么事情

public static void main(String[] args) {
        new Thread(new Runnable() {
            //匿名内部类
            @Override
            public void run() {
                System.out.println("Runnable");
            }
        }){
            //重写
            @Override
            public void run() {
                System.out.println("Thread");
            }
        }.start();
    }
}

从面向对象的思想去考虑:
刚才使用的匿名内部类,然后重写了Thread,
两种,一种是重写run方法,一种是给run传入target

我们看看源码,才能知道为什么调用的是 Thread 类的 run

如果该线程是使用单独的 unnable 运行对象构造的,则调用该 Runnable 对象的run 方法;否则,此方法不执行任何操作并返回。  Thread 的子类应覆盖此方法。
 private Runnable target;
@Override
public void run() {
    if (target != null) {
        target.run();
    }

解释一下 targer 是我们使用 runnable 方法来构建Thread 类时传入的参数。
这个三行代码是什么,大家思考一下,这个是 Thread 类的 run 方法,我们平时使用的新建 Thread 创建线程,就会重写这个方法,即这个三行代码是被重写的,不会调用。但是如果仅仅只是 runnable 新建线程,我们只是重写了 Runnable 接口的 run 方法,启动线程 Thread 调用的就是未被重写的 这三行Thread 类的run方法中的语句。但两者都有时,Thread 的 run 方法就会被前者覆盖,上方的 run 方法中三行代码永远不会执行

小知识:调用start方法,线程就会立刻启动吗?

  1. 调用了 start 方法,线程就立刻启动了吗???

在调用 start 方法后,其实先通知JVM,让其有空闲就创建新线程,但是何时真正创建由线程调度器决定。即,在某些情况下,可能会出现先 start 的线程后执行

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

渣渣高不会写Java

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值