Java多线程(ExecutorService 、Executors、Callable、Future、FutureTask)

前言:我们一般通过继承Thread类重写run方法或者实现runnable接口重写run方法,最后创建和启动一个线程,但是都需要自己创建、启动Thread对象。线程池可以实现帮助我们管理Thread对象,至于要使用几个线程,什么时候启动这些线程,是开启多个线程还是用单个线程来完成这些任务,我们无需操心。

Java通过Executors提供四种线程池

  1.  newCachedThreadPool:创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。(线程最大并发数不可控制)
  2. newFixedThreadPool:创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
  3. newScheduledThreadPool:创建一个定长线程池,支持定时及周期性任务执行。
  4. newSingleThreadExecutor:创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
package threadTest.executors;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @ClassName CachedThreadPool
 * @Description TODO
 * @Author Kikityer
 * @Date 2018/11/21 19:04
 * @Version 1.0.0
 **/
public class CachedThreadPool {
    public static void main(String[] args) {
        ExecutorService exec = Executors.newCachedThreadPool();
        for (int i = 0;i < 5; i++){
            exec.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+" is doing task");
                }
            });
        }
        exec.shutdown();

    }
}


//结果:
/**
 *pool-1-thread-1 is doing task
 *pool-1-thread-2 is doing task
 *pool-1-thread-3 is doing task
 *pool-1-thread-4 is doing task
 *pool-1-thread-5 is doing task
 */

通过for循环建立了5个任务,通过submit方法开启了五个线程处理这五个任务。

submit有两个常用的方法:

Future<?> submit(Runnable task)           接收Runnable类型入参,而Runnable无返回值 。

<T> Future<T> submit(Callable<T> task)        接收Callable类型入参 ,Callable入参允许任务返回值。

package threadTest.executors;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

/**
 * @ClassName CallableAndFuture
 * @Description TODO
 * @Author Kikityer
 * @Date 2018/11/21 19:20
 * @Version 1.0.0
 **/
public class CallableAndFuture {
    public static void main(String[] args) {
        ExecutorService exec = Executors.newCachedThreadPool();
        List<Callable<String>> taskList = new ArrayList<>(); //任务列表,用于存放任务
        for (int i = 0; i < 5; i++){
            taskList.add(new MyCallable()); //创建5个任务,并且添加到列表中
        }

        List<Future<String>> resultList = new ArrayList<>(); //结果列表,用于存放任务执行的结果


        try {
            resultList = exec.invokeAll(taskList);  //invokeAll是等所有任务完成后返回代表结果的Future列表
            exec.shutdown();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        for (Future<String> future : resultList){  //遍历结果列表,打印出每个任务的结果,即打印出每个当前线程的名字(String类型)
            try {
                System.out.println(future.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }

    }
}


/**
 * MyCallable实现Callable接口,创建一个可以有返回值的任务类
 */
class MyCallable implements Callable<String>{
    @Override
    public String call() {
        return Thread.currentThread().getName();
    }
}


//结果:
/**
 *pool-1-thread-1
 *pool-1-thread-2
 *pool-1-thread-3
 *pool-1-thread-4
 *pool-1-thread-5
 */

在上面的代码中,我们创建了一个类实现了一个Callable接口,此类是一个有返回值的任务类。而后开启了一个线程池用来管理线程。从结果可以看出我们得到了任务类所返回的信息。其中用到了Future接口、invokeAll方法、get方法。

Future接口

Future 表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。上面的例子中exec执行器执行了一个Callable类型的任务列表然后得到了Futuer类型的结果列表resultList。

invokeAll方法

invokeAll批量运行所有任务。和submit的区别是:submit提交单个任务

get方法

等待计算完成,获取其结果。get方法会阻塞直到结果返回。

FutureTask

FutureTask类是 Future 接口的一个实现。FutureTask类实现了RunnableFuture接口,RunnableFuture继承了Runnable接口和Future接口所以:

可以看出FutureTask可以当作一个有返回值的Runnable任务来用。 

  • FutureTask可以作为Runnable被线程执行
  • 可以作为Future得到传入的Callable对象的返回值     如:futureTask.get()

猜你喜欢

转载自blog.csdn.net/weixin_40581455/article/details/84328194