问题 了解未来/线程


我试图第一次使用期货。你可以取消一份工作似乎很聪明,但它没有按预期工作。在下面的示例中,仅取消第一个作业。其余的都完成了。我误解了期货的使用吗?

public class ThreadExample 
{
    public static void main(String[] args) throws InterruptedException, ExecutionException 
    {
        int processors = Runtime.getRuntime().availableProcessors();
        System.out.println("Processors: " + processors);
        ExecutorService es = Executors.newFixedThreadPool(processors);
        int nowork = 10;
        Future<Integer>[] workres = new Future[nowork];
        for(int i = 0; i < nowork; i++)
        {
            workres[i] = es.submit(new SomeWork(i));
        }
        for(int i = 0; i < nowork; i++) 
        {
            if(i % 2 == 0)
            {
                System.out.println("Cancel");
                workres[i].cancel(true);
            }
            if(workres[i].isCancelled())
            {
                System.out.println(workres[i] + " is cancelled");
            }
            else
            {
                System.out.println(workres[i].get());
            }
        }
        es.shutdown();
    }
}

class SomeWork implements Callable<Integer> 
{
    private int v;
    public SomeWork(int v) 
    {
        this.v = v;
    }

    @Override
    public Integer call() throws Exception
    {
        TimeUnit.SECONDS.sleep(5);
        System.out.println(v + " done at " + (new Date()));
        return v;
    }
}

输出:

Processors: 4
Cancel
java.util.concurrent.FutureTask@10d448 is cancelled
4 done at Wed May 12 17:47:05 CEST 2010
2 done at Wed May 12 17:47:05 CEST 2010
1 done at Wed May 12 17:47:05 CEST 2010
3 done at Wed May 12 17:47:05 CEST 2010
1
Cancel
2  
3
Cancel
4
5 done at Wed May 12 17:47:10 CEST 2010
7 done at Wed May 12 17:47:10 CEST 2010
8 done at Wed May 12 17:47:10 CEST 2010
6 done at Wed May 12 17:47:10 CEST 2010  
5
Cancel
6
7
Cancel
8
9 done at Wed May 12 17:47:15 CEST 2010  
9

10115
2018-05-12 16:04


起源



答案:


问题是您的取消循环与您的取消循环重叠 get() 循环,阻止。我想你想要2个循环,不是吗?一个循环取消偶数编号的作业,然后是第二个循环,检查哪些取消,哪些取消,然后 get() 因此。

它现在的方式,在循环之前甚至有机会取消 workres[2],它检查并要求 get() 从 workres[1]

所以我认为你需要3个阶段:

1. The `submit()` loop
2. The selective `cancel()` loop
3. The selective `get()` loop (which blocks)

8
2018-05-12 16:13



谢谢!没想到get()是一种阻止问题的方法。 - Mads Andersen


答案:


问题是您的取消循环与您的取消循环重叠 get() 循环,阻止。我想你想要2个循环,不是吗?一个循环取消偶数编号的作业,然后是第二个循环,检查哪些取消,哪些取消,然后 get() 因此。

它现在的方式,在循环之前甚至有机会取消 workres[2],它检查并要求 get() 从 workres[1]

所以我认为你需要3个阶段:

1. The `submit()` loop
2. The selective `cancel()` loop
3. The selective `get()` loop (which blocks)

8
2018-05-12 16:13



谢谢!没想到get()是一种阻止问题的方法。 - Mads Andersen


Future#cancel() 不会终止/中断 已经运行 工作。它只会取消尚未运行的工作。

更新:polygenelubricants确定了根本原因(+1):这是改进的代码:

int processors = Runtime.getRuntime().availableProcessors();
System.out.println("Processors: " + processors);
ExecutorService es = Executors.newFixedThreadPool(processors);
int nowork = 10;
Future<Integer>[] workers = new Future[nowork];

for (int i = 0; i < nowork; i++) {
    final int ii = i;
    workers[i] = es.submit(new Callable<Integer>() {
        public Integer call() throws Exception {
            return ii;
        }
    });
}

for (int i = 0; i < nowork; i++) {
    if (i % 2 == 0) {
        System.out.println("Cancel worker " + i);
        workers[i].cancel(true);
    }
}

for (int i = 0; i < nowork; i++) {
    if (workers[i].isCancelled()) {
        System.out.println("Worker " + i + " is cancelled");
    } else {
        System.out.println("Worker " + i + " returned: " + workers[i].get());
    }
}

es.shutdown();

结果:

处理器:2
取消工人0
取消工人2
取消工人4
取消工人6
取消工人8
工人0被取消
工人1返回:1
工人2被取消
工人3回来了:3
工人4被取消
工人5回来了:5
工人6被取消
工人7回来了:7
工人8被取消
工人9回归:9

(注意它是 workers不是 workres)。


8
2018-05-12 16:11



“如果任务已经启动,则mayInterruptIfRunning参数确定执行此任务的线程是否应该在尝试停止任务时被中断。”如果只能在尚未运行的作业上使用它,Imo取消是一个非常糟糕的单词/方法名:/ - Mads Andersen
我自己发现了它们之间的区别 cancel 和 interrupt (要么 abort 要么 terminate)还不够清楚。 - BalusC
你可以打电话 .cancel(true) 强制开始中断的工作。 - Finbarr
+1给你回复! (OMG我们勾结?) - polygenelubricants
@BalusC:实际上,当你在最后一个循环中打印结果时,无法保证工作完成了工作,即未来可能还没有计算出来?你不必事先等待所有工人的执行结束吗? (或者我误解了什么?) - Bjarke Freund-Hansen