java创建线程的方法

2019-09-27  本文已影响0人  樱花舞

java创建线程有三种方式,继承Thread类,实现Runnable接口,实现Callable接口;但Callable接口与FutureTask配合使用,而FutureTask也是实现Runnable接口,所以也也可以说两种方式。

创建线程

继承Thread类

继承Thread类重写run()方法,不过一般推荐通过实现Runnable来创建线程,因为只能继承一个类,如果想要继承其他类拓展,继承Thread就不是很友好。

public class ThreadOne extends Thread {
    @Override
    public void run() {
        System.out.println("thread one");
    }
}

调用也很简单,只要执行start()方法即可,但如果是执行run()方法,那只是执行普通方法,并不是创建线程执行的。

ThreadOne threadOne = new ThreadOne();
threadOne.start();

实现Runnable接口

public class ThreadTwo implements Runnable {
    @Override
    public void run() {
        System.out.println("thread two");
    }
}

创建Runnable线程需要把类传入Thread实例中。

ThreadTwo threadTwo = new ThreadTwo();
Thread thread = new Thread(threadTwo);
thread.start();

实现Callable接口

这种方式创建线程是可以获得子线程返回结果的,是Runnable的加强版。

public class ThreadThree implements Callable<Long> {
    @Override
    public Long call() throws Exception {
        System.out.println("thread three");
        return 23L;
    }
}

这种方式也需要把实例传入Thread中才能创建线程,执行完之后可以通过FutureTask的get()获取返回结果。

ThreadThree threadThree = new ThreadThree();
FutureTask<Long> future = new FutureTask<>(threadThree);
new Thread(future).start();
//输出返回结果
System.out.println(future.get());

创建线程池

如果在程序中随意创建线程,需要就创建,这是一种不好的方式,不容易管理,代码混乱,同时可能造成一些线程问题。需要用到多线程就必须使用线程池,这是一个好的开发习惯。
线程池主要是实现ExecutorService,而jdk已经实现,可以通过Executors类创建或者new ThreadPoolExecutor来创建,而Executors实际也是通过new ThreadPoolExecutor创建,不过不推荐使用Executors方式创建,至于原因网上很多文章说明,阿里巴巴开发规范也不推荐使用这种方式。
ExecutorService 的submit把要创建的线程提到线程池,由线程池执行,同时可以获取子线程的返回结果。

public class Test {
    private static ExecutorService executorService = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(),
            Runtime.getRuntime().availableProcessors() * 2, 0L,
            TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(512),
            new ThreadPoolExecutor.DiscardPolicy());
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ThreadOne threadOne = new ThreadOne();

        ThreadTwo threadTwo = new ThreadTwo();

        ThreadThree threadThree = new ThreadThree();

        Future<?> a = executorService.submit(threadOne);
        Future<?> b = executorService.submit(threadTwo);
        Future<?> c = executorService.submit(threadThree);
        //输出结果
        System.out.println(a.get());
        System.out.println(b.get());
        System.out.println(c.get());
    }
}

在实际中,经常有这样的场景,大量的数据然后分批次建立子线程来执行,获取子线程执行结果然后统一返回,若线程池以下面的方式操作 就是一种错误方式。在循环中,在线程池中执行子线程,然后把结果保存到集合中,然而这种方式没有多线程效果,在循环中等待子线程执行完之后获取到结果,然后再下一次循环再执行一个子线程。

    public static void errorMethod() throws ExecutionException, InterruptedException {
        List<Long> result = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            ThreadThree threadThree = new ThreadThree();
            //阻塞等待结果,实际相当于等子线程执行完之后然后再创建子线程,然后再执行,没有多线程的效果
            Future<Long> future = executorService.submit(threadThree);
            result.add(future.get());
        }
    }

正确的姿势应该是这样的:

    public static void test() throws InterruptedException, ExecutionException {
        List<Callable<Long>> callables = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            ThreadThree threadThree = new ThreadThree();
            callables.add(threadThree);
        }
        List<Future<Long>> futures = executorService.invokeAll(callables, 10, TimeUnit.SECONDS);
        List<Long> result = new ArrayList<>();
        for (Future<Long> future : futures) {
            result.add(future.get());
        }
    }

先把要执行的子线程保存集合,然后调用用invokeAll方法全部执行,设置超时时间,若在超时时间内没有执行完所有子线程抛出异常。

上一篇 下一篇

猜你喜欢

热点阅读