【Java并发编程篇】创建线程的四种方式

在 Java 中,实现多线程的主要有以下四种:

(1)继承 Thread 类,重写 run() 方法;
(2)实现 Runnable 接口,实现 run() 方法,并将 Runnable 实现类的实例作为 Thread 构造函数的参数 target;
(3)实现 Callable 接口,实现 call() 方法,然后通过 FutureTask 包装器来创建 Thread 线程;
(4)通过 ThreadPoolExecutor 创建线程池,并从线程池中获取线程用于执行任务;

第(1)(2)种方式无法获取线程的执行结果,因为通过重写的 run()  方法的返回值是void;第(3)种方式可以获取线程的执行结果,因为通过 Callable 接口的 call() 方法的返回值是 Object,可以将返回的结果可以放在 Object 对象中;第(4)种方式对于两种情况都支持,具体取决于任务的类型,有返回值的任务必须实现 Callable 接口,无返回值的任务必须实现 Runnable 接口。

继承Thread类的方式

Thread 实现了 Runnable 接口,代表一个线程的实例。启动线程的唯一方法就是通过 Thread 类的start() 方法。start() 方法是一个native方法,它将启动一个新线程,并执行run()方法。

这种方式实现多线程很简单,直接 extends Thread,并重写 run() 方法,就可以启动新线程执行自己定义的run()方法。

通过实现 Runnable 接口

通过实现 Runnable 接口,实现 run() 方法,将 Runnable  接口的实现类的实例作为 Thread 的带参构造函数中,并通过调用 start() 方法启动线程。

实现 Runnable 接口比继承 Thread 类所具有的优势主要有:

① 可以避免 JAVA 中单继承的限制;
② 线程池只能放入实现 Runable 或 Callable类线程,不能直接放入继承 Thread 的类
③ 代码可以被多个线程共享,代码和数据独立,适合多个相同的程序代码的线程去处理同一个资源的情况

实现Callable接口,并通过FutureTask包装器来创建Thread线程

(1)实现 Callable 接口,并实现 call() 方法; 
(2)创建 Callable 接口的实现类的实例,使用 FutureTask 类包装 Callable 对象,该 FutureTask 对象封装了 Callable 对象的 call() 方法的返回值; 
(3)使用 FutureTask 对象作为 Thread 类的构造函数的 target 参数创建并启动线程;
(4)调用 FutureTask 对象的 get() 来获取子线程执行结束的返回值;

public class ThreadDemo03 {
    public static void main(String[] args) {
        Callable<Object> oneCallable = new Tickets<Object>();
        FutureTask<Object> oneTask = new FutureTask<Object>(oneCallable);
        Thread t = new Thread(oneTask);
        System.out.println(Thread.currentThread().getName());
        t.start();
    }
}
class Tickets<Object> implements Callable<Object>{
    //重写call方法
    @Override
    public Object call() throws Exception {
        System.out.println(Thread.currentThread().getName()+"-->我是通过实现Callable接口通过FutureTask包装器来实现的线程");
        return null;
    }   
}

使用 ThreadPoolExecutor 创建线程池

使用 ThreadPoolExecutor 创建线程池,并从线程池中获取线程用于执行任务。

ExecutorService、Callable、Future 实际上都是属于 Executor 框架。线程池支持有返回结果和无返回结果的任务,有返回值的任务必须实现Callable接口,无返回值的任务必须实现Runnable接口。对于有结果的任务,执行 Callable 任务后,可以获取一个 Future 的对象,在该对象上调用 get 就可以获取到Callable任务返回的 Object 了,但需要注意的是:get方法是阻塞的,如果线程未返回结果,那么 get() 方法会一直等待,直到有结果返回或者超时。

 

参考:

 

posted @ 2021-12-22 21:41  残城碎梦  阅读(78)  评论(0编辑  收藏  举报