Java创建并执行线程的四种方法

java里面创建线程有四种方式:
无返回:
1. 实现Runnable接口,重写run();
2. 继承Thread类,重写run();
有返回:
1. 实现Callable接口,重写call(),利用FutureTask包装Callable,并作为task传入Thread构造函数;
2. 利用线程池;

下面来看看具体的创建方式:
1. 继承Thread类,重写run();
创建:创建线程只需要继承Thread类,然后在run方法里写下线程要实现的任务即可;
这里写图片描述
调用:通过调用start方法来启动线程而不能直接调用run方法。
这里写图片描述
① Thread类本质上是实现了Runnable接口,Thread对象代表一个线程的实例。
② Runnable接口只有一个抽象的run()方法。
③ 启动线程的唯一方法就是通过Thread类的start()实例方法。
④ start()方法是一个native方法,它将启动一个新线程,并执行run()方法。
⑤ 自定义类直接extend Thread,并复写run()方法,就可以启动新线程并执行自己定义的run()方法。
2. 创建任务,实现Runnable接口,重写run()。(受欢迎)
因为Java只能单继承,继承了Thread类就不能再继承别的类了所以实现继承更推崇的是让线程类实现Runnable接口。
这里写图片描述
将Runnbale作为参数传入Thread的构造函数,创建Thread.
这里写图片描述
3. Callable接口只包含抽象方法V call()。
这里写图片描述
利用Callable接口创建并启动新线程的步骤:
① 定义MyClass实现Callable接口;Class MyClass implements Callable
② 重写call(),将执行的代码写入;
③ 创建FutureTask的对象;FutureTask中定义了run(),run()内部调用了call(),并保存了call()的返回值;FutureTask futuretask = new FutureTask(newMyClass());
④ 创建Thread的对象;Thread thread = new Thread(futuretask);//传入参数Runnable接口
⑤ 启动线程;thread.start();[图片]
⑥ 可通过FutureTask类的get()方法获得线程执行结束后的返回值,即call的返回值。futuretask.get();

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

public class MyThread {

    public static void main(String[] args) throws InterruptedException {
        FutureTask<Integer> task = new FutureTask<Integer>(new CallableImpl());
        Thread thread = new Thread(task);
        thread.start();
        try {
            System.out.println("task.get() returns " + task.get());
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

class CallableImpl implements Callable<Integer> {

    private static Integer value = 0;

    @Override
    public Integer call() throws Exception {
        System.out.println("执行call方法之前 value = " + value);
        value = value.intValue() + 1;
        System.out.println("执行call方法之后 value = " + value);
        return value;
    }
}

运行结果:
执行call方法之前 value = 0
执行call方法之后 value = 1
task.get() returns 1
4. 通过线程池来创建线程
① new ThreadPoolExecutor(…);
② 创建任务Task implements Callable,重写run()方法;
③ 通过线程池的execute()或submit()将任务command传入线程池;
④ 获取返回值:
1) 实现Callable接口,重写call()方法
class CallableImpl implements Callable
2) 定义线程池
ThreadPoolExecutor executor
3) 利用submit()方法提交任务
Future<?> future = executor.submit(new CallableImpl());
5) 利用FutureTask类get()方法获取返回值
res = task.get();
这里future申明为Future对象,但是它是由FutureTask实现的,也可以直接申明为FutureTask future:

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class MyThreadPool {
    public static void main(String[] args) throws InterruptedException, ExecutionException {

        ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS,
                new ArrayBlockingQueue<Runnable>(20));
        Future task;
        for (int i = 0; i < 5; i++) {
            task = executor.submit(new CallableImpl());
            System.out.println("线程返回结果:" + task.get());
        }
        executor.shutdown();
    }
}

class RunnableImpl implements Runnable {

    @Override
    public void run() {
        // TODO Auto-generated method stub
        System.out.println("hhh");
        System.out.println(Thread.currentThread().getName());
    }
}

运行结果:
执行call方法之前 value = 0
执行call方法之后 value = 1
线程返回结果:1
执行call方法之前 value = 1
执行call方法之后 value = 2
线程返回结果:2
执行call方法之前 value = 2
执行call方法之后 value = 3
线程返回结果:3
执行call方法之前 value = 3
执行call方法之后 value = 4
线程返回结果:4
执行call方法之前 value = 4
执行call方法之后 value = 5
线程返回结果:5

总结:
线程的创建有四种方式:主要分为有返回和无返回,具体根据使用场景来选择。
1. 如果不需要返回且线程数量小,则建议采用实现Runnable接口,重写run()的方式;
2. 如果需要返回且线程数量小,则建议采用实现Callable接口,重写call(),利用FutureTask包装成一个Runnable,再作为参数传入Thread的构造方法的方式创建线程;
3. 如果线程数量较多,则建议采用线程池方式创建:execute提交任务实现无返回操作,submit提交任务实现有返回操作。

补充:FutureTask
JDK1.8中FutureTask实现了RunnableFuture,而RunnableFuture顾名思义就是Runnable接口和Future接口的结合体。因此,FutureTask对象可以作为Runnable对象来用,比如

    Thread thread = new Thread(new FutureTask<V>());

也可以作为Future来用。这就是Java的有魅力的地方。。。

    public class FutureTask<V> implements RunnableFuture<V>
    public interface RunnableFuture<V> extends Runnable, Future<V> {
        void run();
    }

Callable、Future、Runnable、FutureTask的联系与区别见下一篇博文。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章