多线程 - Thread原理及使用

本文详细介绍了Java中创建线程的两种方式:继承Thread类和实现Runnable接口。通过实例代码分析了两者的原理和区别,并探讨了start()与run()方法的不同,强调start()会创建新线程以实现异步执行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

       Java中创建线程有两种方式:继承Thread重写run()与实现Runnable()接口通过Thread构造。

继承Thread重写run()


创建一个TestThread继承自Thread,然后调用start()运行线程

TestThread testThread = new TestThread();
testThread.start();

 public class TestThread extends Thread{
        @Override
        public void run() {
            super.run();
            Log.i("thread","TestThread");
        }
    }

打印的log为:TestThread

I/thread: TestThread


实现Runnable接口


定义一个类TestRunnable实现Runnable的run方法,通过Thread的构造函数传入一个TestRunnable对象,调用start()开启线程

 Thread runnable = new Thread(new TestRunnable());
 runnable.start();

 public class TestRunnable implements Runnable{

      @Override
      public void run() {
          Log.i("thread","testRunnable");
      }
  }

打印的log为:testRunnable

I/thread: testRunnable


继承Thread类与实现Runnable接口的原理和区别


通过上面的两种方式都可以实现多线程,那么将TestRunnable对象传给TestThread会是怎样的呢?代码走起


  TestThread testThread = new TestThread(new TestRunnable());
  testThread.start();

   public class TestThread extends Thread{

        public TestThread(Runnable target) {
            super(target);
        }

        @Override
        public void run() {
            super.run();
            Log.i("thread","TestThread");
        }
    }

    public class TestRunnable implements Runnable{

        @Override
        public void run() {
            Log.i("thread","testRunnable");
        }
    }

运行代码,发现log分别打印了TestThread和testRunnable

I/thread: TestThread
I/thread: testRunnable

而调用Thread的start()函数会创建一个新的线程以及调用线程的run()方法,而在TestThread的run()方法中,调用了super.run(),说明super.run()中调用了TestRunnable的run()方法,那看看Thread中构造函数传入Runnable对象是如何实现的。


public class Thread implements Runnable {

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

    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc) {

       /*省略其他的一些代码*/
        this.target = target;
        setPriority(priority);
        tid = nextThreadID();
    }

    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }
}

可以发现Thread也是实现了Runnable接口,在构造函数时将传入的Runnable对象赋值给了target,而在实现的run()函数中调用了target.run(),即我们构造函数传入的Runnable对象。因此我们可以得到,通过继承Thread和实现Runnable本质是一样的,Thread是通过重写run()函数,而Runnable的方式间接通过Thread的target属性来调用run()函数,都依托于Thread,最终执行run()函数。

start()与run()的区别


通过上面的分析可以得知道Thread实现了Runnable接口,最终执行的也是run()函数,而我们在调用启动线程的时候是start()函数,那么他们有什么区别呢?首先看段代码

TestThread testThread = new TestThread();
testThread.start();
 try {
      Thread.sleep(200);
 } catch (InterruptedException ignored) {
 }
Log.i("thread","TestThread start");

 public class TestThread extends Thread{

        @Override
        public void run() {
            super.run();
            for (int i = 0; i < 10 ; i ++) {
                Log.i("thread","TestThread" + i);
                try {
                    Thread.sleep(200);
                } catch (InterruptedException ignored) {
                }
            }
        }
    }

在TestThread的run()打印10行log,然后在调用start()打印TestThread start,sleep 200毫秒方便看打印顺序,看看打印的结果如何

thread: TestThread0
thread: TestThread start
thread: TestThread1
thread: TestThread2
thread: TestThread3
thread: TestThread4
thread: TestThread5
thread: TestThread6
thread: TestThread7
thread: TestThread8
thread: TestThread9

因为TestThread是多线程执行的,因此TestThread start打印在TestThread0之后是正常的,既然start()间接调用了run(),那么我们直接调用run()会怎样呢?

        TestThread testThread = new TestThread();
        testThread.run();
        try {
            Thread.sleep(200);
        } catch (InterruptedException ignored) {
        }
        Log.i("thread","TestThread start");

输出的log如下

thread: TestThread0
thread: TestThread1
thread: TestThread2
thread: TestThread3
thread: TestThread4
thread: TestThread5
thread: TestThread6
thread: TestThread7
thread: TestThread8
thread: TestThread9
thread: TestThread start

发现TestThread是最后打印的,在执行完TestThread的run()方法之后才执行,变成了同步执行。

看看Thread的start()函数是怎么实现的

    /**
     * Starts the new Thread of execution. The <code>run()</code> method of
     * the receiver will be called by the receiver Thread itself (and not the
     * Thread calling <code>start()</code>).
     *
     * @throws IllegalThreadStateException - if this thread has already started.
     * @see Thread#run
     */
    public synchronized void start() {
        checkNotStarted();

        hasBeenStarted = true;

        nativeCreate(this, stackSize, daemon);
    }

    private native static void nativeCreate(Thread t, long stackSize, boolean daemon);

在start()函数中调用了nativeCreate来创建一个线程,而start()的注释中也写明,创建一个新的线程,run()函数的代码在新的线程里面执行,而start()函数的线程在调用start()的线程里面执行。
根据上面的分析可以得到,直接调用run()函数没有创建新的线程,而是在调用的线程中执行,因此变成了同步执行;而调用start()函数,start()函数会在调用的线程中执行,还是属于同步执行,但是start()函数会创建一个新的线程来执行run()函数,因此我们通过继承Thread还是实现Runnable重写run()函数都会在新的线程执行,从而变成异步执行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值