Future案例一直阻塞问题探究

本案例是学习自《Java并发编程之美》第十一章第九小节整理总结而来!推荐大家学习此书!

代码案例

我们设定一个单线程线程池并且队列设置为只能容纳一个任务,否则就执行DiscardPolicy的拒绝策略!然后开始执行下面代码,看看问题所在!

public class FutureDemo {

    public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(1,
                1,
                1l,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(1),
                new ThreadPoolExecutor.DiscardPolicy());

        Future future1 = executor.submit(new Runnable() {
            @SneakyThrows
            @Override
            public void run() {
                System.out.println("start task 1");
                Thread.sleep(1000);
            }
        });

        Future future2 = executor.submit(new Runnable() {
            @SneakyThrows
            @Override
            public void run() {
                System.out.println("start task 2");
            }
        });


        Future future3 = null;
        try {
            future3 = executor.submit(new Runnable() {
                @SneakyThrows
                @Override
                public void run() {
                    System.out.println("start task 3");
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println("return task 1 :" + future1.get());
        System.out.println("return task 2 :" + future2.get());
        System.out.println("return task 3 :" + future3 == null ? null : future3.get());

        executor.shutdown();
    }
}

运行结果,执行完前两个任务后,第三个任务一直处于阻塞状态:
在这里插入图片描述
如果去掉future3.get()后,就不会发生阻塞问题了:
在这里插入图片描述

问题分析

当使用DiscardPolicy或者DiscardOldestPolicy策略的时候,调用future的get方法时会出现一直阻塞的问题!看源码我们得知,是由于任务三的状态=0,小于COMPLETING(1),所以一直阻塞,无法继续执行report方法:

public V get() throws InterruptedException, ExecutionException {
    int s = state;
    // 小于1   就会一直阻塞
    if (s <= COMPLETING)
        s = awaitDone(false, 0L);
    return report(s);
}


/**
 * FutureTask是有状态的,初始状态就是NEW
 *
 * Possible state transitions:
 * NEW -> COMPLETING -> NORMAL
 * NEW -> COMPLETING -> EXCEPTIONAL
 * NEW -> CANCELLED
 * NEW -> INTERRUPTING -> INTERRUPTED
 */
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;

由submit方法进入也是可以窥见的,Runnable转化为FutureTask,state = NEW

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

// submit-1 
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
    return new FutureTask<T>(runnable, value);
}
public FutureTask(Runnable runnable, V result) {
    this.callable = Executors.callable(runnable, result);
    this.state = NEW;       // ensure visibility of callable
}

继续看一下线程池如何执行一个任务的:

// submit-2
public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
    
    int c = ctl.get();
    // 线程个数小于核心线程数  创建线程执行
    if (workerCountOf(c) < corePoolSize) {
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    // 目前线程数=核心线程数,任务放入队列
    if (isRunning(c) && workQueue.offer(command)) {
        int recheck = ctl.get();
        if (! isRunning(recheck) && remove(command))
            reject(command);
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
    // 尝试新增线程执行任务,失败就拒绝任务
    else if (!addWorker(command, false))
        reject(command);
}

看一下拒绝策略DiscardPolicy ,就是什么都不做,所以放弃的任务状态仍处于NEW,因此,当拒绝策略没有处理FutureTask的状态时,或者没有throw 错误的时候就会一直阻塞状态:

  public static class DiscardPolicy implements RejectedExecutionHandler {
    /**
     * Creates a {@code DiscardPolicy}.
     */
    public DiscardPolicy() { }

    /**
     * Does nothing, which has the effect of discarding task r.
     *
     * @param r the runnable task requested to be executed
     * @param e the executor attempting to execute this task
     */
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    }
}

最佳实践

自定义拒绝策略:

public class FutureDemo1 {

    public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(1,
                1,
                1l,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(1),
                new MyPolicy());

        Future future1 = executor.submit(new Runnable() {
            @SneakyThrows
            @Override
            public void run() {
                System.out.println("start task 1");
                Thread.sleep(1000);
            }
        });

        Future future2 = executor.submit(new Runnable() {
            @SneakyThrows
            @Override
            public void run() {
                System.out.println("start task 2");
            }
        });


        Future future3 = null;
        try {
            future3 = executor.submit(new Runnable() {
                @SneakyThrows
                @Override
                public void run() {
                    System.out.println("start task 3");
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println("return task 1 :" + future1.get());
        System.out.println("return task 2 :" + future2.get());
        try {
            System.out.println("return task 3 :" + future3 == null ? null : future3.get());
        } catch (Exception e) {
            e.printStackTrace();
        }


        executor.shutdown();
    }

    static class MyPolicy implements RejectedExecutionHandler {

        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            ((FutureTask)r).cancel(true);
        }
    }
}

在这里插入图片描述

或者使用超时方法:

try {
    System.out.println("return task 3 :" + future3 == null ? null : future3.get(5, TimeUnit.SECONDS));
} catch (Exception e) {
    e.printStackTrace();
}

在这里插入图片描述
或者使用AbortPolicy策略,如下:
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值