并发系列之「Java中创建线程的三个方式」

Java中有三种线程创建方式,分别为

  • 继承Thread类并重写run()方法

  • 实现Runnable接口的run()方法

  • 使用FutureTask方式

继承Thread类并重写run()方法

/**
 * @Author Hory
 * @Date 2020/10/5
 */
public class ThreadTest {
    
    
    public static void main(String[] args) {
    
    
        MyThread thread = new MyThread();
        thread.start();
    }
    public static class MyThread extends Thread {
    
    
        @Override
        public void run(){
    
    
            System.out.println("I am a child Thread");
          	System.out.println(this);
        }
    }
}

运行结果:

I am a child Thread
Thread[Thread-0,5,main]

当创建完thread对象后该线程并没有被启动执行,直到调用了start()方法后才真正启动了线程。

其实调用start()方法后线程并没有马上执行而是处于就绪状态,这个就绪状态是指该线程已经获取了除CPU资源外的其他资源,等待获取CPU资源后才会真正处于运行状态。

一旦run()方法执行完毕,该线程就处于终止状态。

使用继承方式的优点

使用继承方式的好处是,在run()方法内获取当前线程直接使用this就可以了,无须使用Thread.currentThread()方法

使用继承方式的缺点

不好的地方是Java不支持多继承,如果继承了Thread类,那么就不能再继承其他类。

另外任务与代码没有分离,当多个线程执行一样的任务时需要多份任务代码,而Runable则没有这个限制。

下面看实现Runnable接口的run()方法方式。

实现Runnable接口的run()方法

/**
 * @Author Hory
 * @Date 2020/10/5
 */
public class RunnableTask implements Runnable {
    
    

    public static void main(String[] args) {
    
    
        RunnableTask task = new RunnableTask();
        new Thread(task).start();
        new Thread(task).start();
    }

    @Override
    public void run(){
    
    
        System.out.println("I am a child Thread");
        System.out.println(this);
    }
}

如上面代码所示,两个线程共用一个task代码逻辑,如果需要,可以给RunableTask添加参数进行任务区分。

另外,RunableTask可以继承其他类。

但是上面介绍的两种方式都有一个缺点,就是任务没有返回值。

下面看最后一种,即使用FutureTask的方式。

使用FutureTask方式

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * @Author Hory
 * @Date 2020/10/5
 */
public class CallerTask implements Callable<String> {
    
    

    public static void main(String[] args) throws InterruptedException{
    
    
        // 创建异步任务
        FutureTask<String> futureTask = new FutureTask<>(new CallerTask());
        new Thread(futureTask).start();
        try{
    
    
            // 等待任务执行完毕,并返回结果
            String result = futureTask.get();
            System.out.println(result);
        }catch (ExecutionException e){
    
    
            e.printStackTrace();
        }
    }

    @Override
    public String call() throws Exception {
    
    
        return "hello";
    }
}

如上代码中的CallerTask类实现了Callable接口的call()方法。

main函数内首先创建了一个FutrueTask对象(构造函数为CallerTask的实例),然后使用创建的FutrueTask对象作为任务创建了一个线程并且启动它,最后通过futureTask.get()等待任务执行完毕并返回结果。

总结

使用继承方式的好处是方便传参,我们可以在子类里面添加成员变量,通过set方法设置参数或者通过构造函数进行传递,而如果使用Runnable方式,则只能使用主线程里面被声明为final的变量。

不好的地方是Java不支持多继承,如果继承了Thread类,那么子类不能再继承其他类,而Runable则没有这个限制。

前两种方式都没办法拿到任务的返回结果,但是Futuretask方式可以。

猜你喜欢

转载自blog.csdn.net/weixin_44471490/article/details/108934481
今日推荐