【并发那些事 】建立线程的三种方式

建立线程能够说是并发知识中最基础的操做了,JDK 提供的建立线程的方式,若是不包括经过线程池的话,目前有三种形式,它们分别是经过继承 Thread 类,经过实现 Runable 接口,经过 FutureTask。以下图所示java

下面整理了一下 3 种方法的具体使用与异同。多线程

建立线程的 3 种方法

1. 继承 Thread

  1. 建立一个类继承 Thread 并覆盖 run 方法
class MyThread extends Thread {
    @Override
    public void run() {
        String threadName = getName();
        for (int i = 0; i < 20; i++) {
            System.out.println("线程[" + threadName + "]运行开始,i = " + i + " time = " + new Date());
        }
    }
}
  1. 建立并启动线程
MyThread myThread1 = new MyThread();
        MyThread myThread2 = new MyThread();

        myThread1.start();
        myThread2.start();

        String threadName = Thread.currentThread().getName();
        for (int i = 0; i < 20; i++) {
            System.out.println("线程[" + threadName + "]运行开始,i = " + i + " time = " + new Date());
        }

总体流程以下:并发

这里步骤比较简单和清晰异步

  1. 建立一个类,这类要继承 Thread
  2. 覆盖 Thread 的 run 方法,并在此方法中实现你的多线程任务
  3. 建立这个类的实例
  4. 调用它的 start() 方法(这里要注意,新手容易直接调用 run 方法,那样只是普通调用,而很少线程调用)

2. 实现 Runable

  1. 建立一个类实现 Runable 接口,并覆盖 run 方法
class MyRunable implements Runnable {
    
    @Override
    public void run() {
        String threadName = Thread.currentThread().getName();
        for (int i = 0; i < 20; i++) {
            System.out.println("线程[" + threadName + "]运行开始,i = " + i + " time = " + new Date());
        }
    }
}
  1. 建立类的实现,并将它传给 Thread 的构造函数来建立 Thread 
MyRunable myRunable = new MyRunable();

new Thread(myRunable).start();
new Thread(myRunable).start();

String threadName = Thread.currentThread().getName();
for (int i = 0; i < 20; i++) {
    System.out.println("线程[" + threadName + "]运行开始,i = " + i + " time = " + new Date());
}

总体流程以下:ide

具体步骤以下:函数

  1. 建立一个实现了 Runable 接口的类
  2. 覆盖 run 方法,并在此方法中实现你的多线程任务
  3. 建立 Runable 接口实现类的实例
  4. 经过把上步获得的 Runable 接口实现类的实例,传给  Thread 的有参构造函数来建立 Thread 的实例
  5. 调用上步得来的 Thread 实例的 start() 方法(这里要注意,新手容易直接调用 run 方法,那样只是普通调用,而很少线程调用)

3. 经过 FutureTask

  1. 建立一个实现了 Callable<t> 接口的类,并实现call 方法 (T 表明你想要的返回值类型)
class MyCallerTask implements Callable<string> {
    
    @Override
    public String call() throws Exception {
        System.out.println("执行任务开始");
        Thread.sleep(3000);
        System.out.println("执行任务结束");
        return "hello";
    }
}
  1. 建立并启动线程
// 建立异步任务
        FutureTask<string> futureTask = new FutureTask&lt;&gt;(new MyCallerTask());
        // 启动线程
        new Thread(futureTask).start();
        System.out.println("其它操做");
        try {
            // 等待任务执行完,并得到任务执行完的结果
            String result = futureTask.get();
            System.out.println(result);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

总体流程以下:线程

具体步骤以下:3d

  1. 建立一个实现了 Callable<t> 接口的类,这里 T 的类型就是你线程任务想要返回的类型
  2. 覆盖 call 方法,并在此方法中实现你的多线程任务
  3. 建立 Callable 接口实现类的实例
  4. 经过把上步获得的 Callable 接口实现类的实例,传给  FutureTask 的有参构造函数来建立 FutureTask 的实例
  5. 经过把上步获得的 FutureTask 实例,传给  Thread 的有参构造函数来建立 Thread 的实例
  6. 调用上步得来的 Thread 实例的 start() 方法(这里要注意,新手容易直接调用 run 方法,那样只是普通调用,而很少线程调用)
  7. 若是你还想得到返回值,须要再调用 **FutureTask **实例的 get() 方法(这里要注意,get()会阻塞线程)

3种方法的优缺点

经过上述的演示代码,能够看出这 3 种方法,其实各有优缺点code

复杂程度

经过代码量与逻辑能够明显感受出来,第一种直接继承 Thread 最方便,而且其它两种到最后,仍是要依赖建立 Thread 才能实现。因此从方便及难易程度来看,能够获得以下结论: blog

可扩展性

经过演示代码能够看出,只有第一种是经过继承,其它两种是经过实现接口的形式。咱们都知道 JAVA 是不容许多继承,可是能够多实现。因此若是使用了第一种方法,就没法再继承别的类了。另外第一种把线程与线程任务冗余在了一块儿,不利于后期的维护。因此能够获得以下结论:

是否有返回值

从代码中能够很容易看出,只有经过 FutureTask 的方式才有返回值,另外两种均没有,因此得出以下结论

该用哪一种方式建立线程

若是要用到返回值,那不用想,确定只能使用 FutureTask 的方法。若是对于返回值没有要求,那Thread 与  Runable 都可,不过,考虑到可扩展性,最好使用 Runable 的形式。不过,话说回来,若是在真正项目中使用,综合考虑,通常仍是最推荐经过线程池去建立。

> 本文由博客一文多发平台 OpenWrite 发布! </t></string></string></t>

相关文章
相关标签/搜索