Future,FutureTask,线程池ThreadPoolExecutor

目录

 

1、Future接口

2、FutureTask

3、线程池

3.1、ThreadPoolExecutor 线程池

3.2、几个特色线程池


1、Future接口

Future 是配合用Callable实现的线程的,Callable实现的线程可以有返回值,必须等到线程执行完了才会返回值,难道我们要一直等它执行完,岂不是阻塞浪费。因此才用Future来异步获取线程的返回值,不用等待。

boolean cancel(boolean mayInterruptIfRunning):尝试去取消线程,如果无法取消(因为已经执行完毕、或者已经被取消了,或者因为其它原因),则返回false,如果成功取消,则返回true。

boolean isCancelled():线程在正常完成之前是否已经被取消。

boolean isDone():如果线程已经完成,则返回true。

V get():获取线程返回的值,如果该线程已经被取消,则抛出异常 CancellationException,如果线程抛出异常,则抛出异常 ExecutionException,如果该线程被中断了,则抛出异常 InterruptedException。

V get(long timeout, TimeUnit unit):获取线程返回的值,如果在指定时间内线程还没有返回值,则抛出异常超时异常, 如果该线程已经被取消,则抛出异常 CancellationException,如果线程抛出异常,则抛出异常 ExecutionException,如果该线程被中断了,则抛出异常 InterruptedException。

public interface Future<V> {
    boolean cancel(boolean mayInterruptIfRunning);
    boolean isCancelled();
    boolean isDone();
    V get() throws InterruptedException, ExecutionException;
    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

2、FutureTask

FutureTask 相当于 Callable 和 Future 的结合,将 Callable 实现的线程进行一层封装,提供很多能够方便操作线程的方法,包括获取线程的返回值,取消线程等等。把线程包装成一个FutureTask。

FutureTask 根据线程的执行情况分为几个状态:NEW(0,创建状态),COMPLETING(1,运行中),NORMAL(2,正常结束),EXCEPTIONAL(3,异常结束),CANCELLED(4,被取消),INTERRUPTING(5,中断中,我想是将线程的中断标志置为了中断),INTERRUPTED(6,已被中断)。

工作原理:FutureTask 用于封装一个 Callable 对象,用于生产线程,FutureTask 可以对线程进行一些操作。get() 获取线程的返回值(线程正常结束下),如果线程在执行期间出现了异常,或者被中断了,get方法都会抛出相应的异常。isCancelled() 和 isDone() 方法都是用于判断线程状态的,它会根据当前的状态值直接返回结果。cancel 方法用于取消线程,如果线程已经被取消了,或者线程因为中断或者异常结束了,那自然没法取消了,则返回false,如果线程还处于NEW阶段,则可以直接取消线程,不让它运行了,如果线程已经在运行了,那就看参数 mayInterruptIfRunning 的值了,如果为 true,表示要去中断正在运行的线程以达到取消线程的目的(可能会中断成功,也可能中断不成功,因为中断只是将线程的中断标志置中断状态,线程在运行时是不会去检查中断标志的,所以如果线程在运行,那么cancel 的中断是不起作用的,只有线程在阻塞时,才检查中断标志,因此,只有当线程处于阻塞状态时,cancel的中断才会真地中断线程)。

FutureTask 实现了RunnableFuture接口,RunnableFuture接口继承了 Runnable 接口,而 FutureTask 有实现了 run 方法,其实 FutureTask 就相当于一个线程对象了(因此,Callable 方式创建线程的代码如下),只不过这个对象内部还包含了一个 Callable 对象。  值得注意的是:start 方法是调用的 ft 的 run 方法,只是 ft 的 run 方法也是调用的 Callable 对象的 call 方法。

public class Cuthair implements Callable<String>{
    @Override
    public String call(){
        System.out.println("理完发了。");
        return "OK";
    }
}

public class Test{
    public static void main(String[] args){
        Cuthair c = new Cuthair();
        FutureTask<String> ft = new FutureTask<>(c);
        Thread thread = new Thread(ft);
        thread.start();  // start 方法也是调用的 ft 的 run 方法
        System.out.println("理发线程的返回值是:" + ft.get());
    }
}

构造方法:

1、public FutureTask(Callable<V> callable):// 构造方法,指定Callable对象,初始化状态NEW。

2、public FutureTask(Runnable runnable, V result):// 构造方法,指定Runnable对象,指定线程返回值,初始化状态NEW,按理说FutureTask只接受Callable对象,为了能够接受Runnable对象,使用一个适配器,使得FutureTask能够接受Runnable对象,由于Runnable对象生成的线程没有返回值,所以要指定返回值 result。

常用方法:

1、 public V get() :// 如果线程已经运行完毕,则获取返回值,如果线程出现中断、异常等,就会抛出异常,如果线程还未执行完毕,则将当前调用get方法的线程加入到此FutureTask的阻塞等待队列中。

2、public boolean isCancelled():// 如果此线程在正常完成之前被取消,则返回true

3、public boolean isDone():// 如果线程已经开始了,就返回true

4、public boolean cancel(boolean mayInterruptIfRunning):// 取消此线程,如果取消成功,返回true,否则,返回false,mayInterruptIfRunning = true 表示如果线程还未结束,则中断它。

5、public V get(long timeout, TimeUnit unit):// 在指定的时间内获取返回值,如果能够获取到,则返回值,如果不能,可能是因为线程中断了,或者异常了,则抛出异常,如果是因为超过了指定时间而没有获取到值,则抛出超时异常。

public class FutureTask<V> implements RunnableFuture<V> {
    private volatile int state;
    private static final int NEW          = 0;
    private static final int COMPLETING   = 1;
    private static final int NORMAL       = 2;
    private static final int EXCEPTIONAL  = 3;
    private static final int CANCELLED    = 4;
    private static final int INTERRUPTING = 5;
    private static final int INTERRUPTED  = 6;
    // Callable对象
    private Callable<V> callable;
    // 线程返回值
    private Object outcome; 
    // Callable生成的线程
    private volatile Thread runner;
    // 阻塞等待获取返回值的线程队列
    private volatile WaitNode waiters;
    // 
    @SuppressWarnings("unchecked")
    private V report(int s) throws ExecutionException {
        Object x = outcome;
        if (s == NORMAL)
            return (V)x;
        if (s >= CANCELLED)
            throw new CancellationException();
        throw new ExecutionException((Throwable)x);
    }
    // 构造方法,指定线程,初始化状态
    public FutureTask(Callable<V> callable) {
        if (callable == null)  throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;  
    }
    // 构造方法,指定Runnable实现的线程,指定线程返回值,初始化状态
    public FutureTask(Runnable runnable, V result) {
        this.callable = Executors.callable(runnable, result);
        this.state = NEW;       
    }
    // 如果此线程在正常完成之前被取消,则返回true
    public boolean isCancelled() {    return state >= CANCELLED;   }
    // 如果线程结束了,就返回true
    public boolean isDone() {   return state != NEW;   }
    // 取消此线程,如果取消成功,返回true,否则,返回false
    public boolean cancel(boolean mayInterruptIfRunning) {
        if (!(state == NEW &&
              UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
                  mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
            return false;
        try {    
            if (mayInterruptIfRunning) {
                try {
                    Thread t = runner;
                    if (t != null)
                        t.interrupt();
                } finally { 
                    UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
                }
            }
        } finally {
            finishCompletion();
        }
        return true;
    }
    // 如果线程已经运行完毕,则获取返回值
    public V get() throws InterruptedException, ExecutionException {
        int s = state;
        if (s <= COMPLETING)
            s = awaitDone(false, 0L);
        return report(s);
    }
    // 在指定的时间内获取返回值,否则抛出异常
    public V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException {
        if (unit == null)
            throw new NullPointerException();
        int s = state;
        if (s <= COMPLETING &&
            (s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)
            throw new TimeoutException();
        return report(s);
    }
    static final class WaitNode {
        volatile Thread thread;
        volatile WaitNode next;
        WaitNode() { thread = Thread.currentThread(); }
    }
    // run方法,相当于一个线程的run方法
    public void run() {
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    set(result);
            }
        } finally {
            runner = null;
            int s = state;
            if (s >= INTERRUPTING)       handlePossibleCancellationInterrupt(s);
        }
    }
}

3、线程池

3.1、ThreadPoolExecutor 线程池

Executors 类里面有很多个创建各种线程池的方法,但是都是基于 ThreadPoolExecutor 创建的,意思就是那些各种各样的线程池本质上都是 ThreadPoolExecutor。

构造方法,把这个构造方法弄明白了,基本上原理就清楚了:

1、下面这个构造方法是参数最全的构造方法,所有的参数都需要我们指定,接下来看看每个参数代码什么意思

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)

corePoolSize:线程池的核心池大小(核心线程数)。

maximumPoolSize:线程池的最大线程数。

keepAliveTime:线程最大空闲时间。

unit:线程最大空闲时间的时间单位。

workQueue:线程阻塞队列。

threadFactory:线程工厂,用于创建线程补充线程池,默认为 Executors.defaultThreadFactory() 。

handler:拒绝策略,默认为 AbortPolicy,直接抛弃策略。

在线程池刚创建的时候,池子里一个线程都没有,是空池,当有任务来的时候,才开始一条线程一条线程的创建出来运行任务,在线程数量没有到达 corePoolSize 之前,只要是有任务来,就会创建新线程(即便当前池子里有空闲线程也不用),当线程数量增加到 corePoolSize 时,就算到一个阶段了,暂时不会再增加线程数量了,当继续有任务来时,如果当前 corePoolSize 个线程能应付的话,就不会新建线程了,当没有任务时,这 corePoolSize 个线程也不会销毁,仍然会一直存在。

当任务量非常大, corePoolSize 个线程已经无法处理的时候,会将多出的任务按照FIFO的方式放到 workQueue 阻塞队列中,当线程池里有空闲线程了,就从队列中取出任务执行。

当任务量继续增大,corePoolSize 个线程已经用完,而且阻塞队列也占满了,此时再来任务怎么办呢?继续一条一条地创建线程,增加线程数量,但是池子里的总线程数不能超过 maximumPoolSize,如果能够应付了,就不再创建新的线程了,当任务量减少时,池子里就会出现一些空闲的线程,当线程的空闲时间超过了 keepAliveTime 时,就会将空闲线程销毁,但是必须保证池子里的线程数量不低于 corePoolSize。

当任务量还在继续增大,corePoolSize 个线程用完了,阻塞队列占满了, 池子里的线程数量也达到了最大值maximumPoolSize,此时再来任务,怎么办呢?就看拒绝策略handler 是什么了,有 4 种策略: AbortPolicy(默认策略,接抛出异常),CallerRunsPolicy(用调用者所在的线程来执行任务),DiscardOldestPolicy(将阻塞队列头的任务删除,将此任务插入队列头),DiscardPolicy(不处理,直接丢弃掉)。

2、ThreadPoolExecutor 还有一些可用的方法,大部分都是 set 和 get 方法用于操作线程池的属性,还可以设置核心线程在超过最大空闲时间会被销毁。

3、阻塞队列的设置是比较重要的,因为有多种阻塞队列,看你用哪种,阻塞队列参考 https://blog.csdn.net/dgh112233/article/details/106296319,假如你自定义了一个实现了Runable的线程类,线程类有优先级,想要线程池按照优先级来运行任务,那就可以用 PriorityBlockingQueue 阻塞队列作为线程池的阻塞队列,这样,每次从队列里获取的线程就是当前优先级最高的线程。

4、怎么使用线程池呢?

举个例子:

public class ImplementsCallable implements Callable<String> {
    @Override
    public String call() throws InterruptedException {
        Thread.sleep(2000);
        return "哈哈,我是线程返回值";
    }
    public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
        LinkedBlockingQueue<Runnable> queue = new LinkedBlockingQueue<>(4);
        ThreadPoolExecutor threadPoolExecutor =
                new ThreadPoolExecutor(3, 6, 5000, TimeUnit.MICROSECONDS, queue, Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());
        Future<String> s = threadPoolExecutor.submit(new ImplementsCallable());
        System.out.println(s.get(1000, TimeUnit.MICROSECONDS));
    }
}

创建线程池,指定几个参数。

提交任务,重点就在于提交任务,交给线程池去执行,下面看看 submit 方法的几种形式:

public Future<?> submit(Runnable task) 方法提交一个 Runnable 任务时,由于没有返回值,默认返回值为 null。

        newTaskFor(task, null): 用于将 Runnable 形式的任务封装成 FutureTask。

        execute(ftask):用于让线程池执行任务。

    public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }

public <T> Future<T> submit(Runnable task, T result) 方法提交一个 Runnable 任务,指定返回值。

    public <T> Future<T> submit(Runnable task, T result) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task, result);
        execute(ftask);
        return ftask;
    }

public <T> Future<T> submit(Callable<T> task) 方法提交一个 Callable 任务。

    public <T> Future<T> submit(Callable<T> task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task);
        execute(ftask);
        return ftask;
    }

因此,得出结论,能够向线程池提交 Runnable 形式,Callable 形式的任务,都被封装为 FutureTask 的形式交给线程池去执行,提交之后,submit 方法立即返回该 FutureTask 任务的引用,随后我们可以通过这个 FutureTask 引用来对该任务线程进行一些操作或者获取任务线程的返回值。

3.2、几个特色线程池

Executors 类里面有几个静态方法,用于创建具有特色的线程池,接下来挨个看看。

1、固定线程数量的线程池,核心线程数 和 最大线程数 一样,说明不会有线程因为空闲时间超过被销毁,因此,最大空闲时间也就设置为 0 ,阻塞队列是链表阻塞队列,默认使用线程工厂,默认使用抛出异常拒绝策略。

    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

2、固定线程数量的线程池,和上面一样,不过能够指定核心线程数 和 线程工厂。

public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory)

3、单线程的线程池。线程池里就只有一个线程,其它都是默认,阻塞队列是链表阻塞队列。

    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

4、单线程的线程池。和上面一样,可以指定线程工厂。

public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory)

5、缓存性质的线程池。几个参数就不再介绍了,这个线程池是按需创建线程,有多少任务就创建多少线程,当空闲的时候,无论线程池里有多少空闲超时了的线程,都会被销毁,哪怕销毁完了也要销毁,阻塞队列用的是同步队列,队列里面不存储任务,相当于队列为空,没到达一个任务,线程池就需要分配一个线程去执行,或者创建一个新线程去执行。总结,来一个任务我就分配一个线程去执行,如果线程不够,就创建一个新线程去执行,线程池里的线程数量可以无限,超时的空闲线程会被销毁。

    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

6、缓存性质的线程池,和上面一样,可以指定线程工厂。

public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值