并发编程中的一致性问题:ForkJoinPool调用shutdown的bug.(jdk8&jdk9)

ForkJoinPool调用shutdown从而终止整个并发执行框架。

包括取消所有队列中已有的任务,终止所有的工作线程。

考虑这样一个场景,一个线程正在提交任务,另一个线程正在调用shutdown终止线程池。

此时涉及到3个独立的执行逻辑:

  • 调用pool.submit的线程
  • 调用pool.shutdown()的线程
  • 线程池中的工作线程
由于这三个独立的执行逻辑,必须就线程池将(SHUTDOWN)这一状态达成共识。有没有可能会产生不一致的状态,从而导致丢失信号呢?
在当前的jdk8中的实现存在这个bug,JDK 9 Early-Access中也存在这个问题,只是重现方法不同,见下文。

我们使用如下的test case(jdk8):
package com.psly;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
public class TestForkJoin {

	public static void main(String[] args) throws InterruptedException {
		// TODO Auto-generated method stub
		ForkJoinPool pool = new ForkJoinPool();
		new Thread(){
			public void run(){
				ForkJoinTask<String> task = pool.submit(new Callable<String>(){
					public String call(){
						return "Hello,world";
					}
				});
				//输出 Hello,world  (永远不会输出,也不会报异常, 所以这是个bug)
				try {
					System.out.println(task.get());
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (ExecutionException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			{ this.setName("printHelloWorld");this.start(); }
		};
	//	Thread.sleep(1000);
		new Thread(){
			public void run(){
				pool.shutdown();
			}
			{ this.setName("shutdownPool");this.start(); }
		};
	}
}
执行以上代码,输出为:
Exception in thread "printHelloWorld" java.util.concurrent.RejectedExecutionException
	at java.util.concurrent.ForkJoinPool.externalSubmit(ForkJoinPool.java:2328)
	at java.util.concurrent.ForkJoinPool.externalPush(ForkJoinPool.java:2419)
	at java.util.concurrent.ForkJoinPool.submit(ForkJoinPool.java:2675)
	at com.psly.TestForkJoin$1.run(TestForkJoin.java:14)
任务被拒绝,这是一个可接受的结果。
//	Thread.sleep(1000);
我们去除这一句注释,让shutdown线程晚1秒一点执行。
再次执行,输出为:
Hello,world
这同样是一个可以接受的结果。
也就是线程池,面对提交的一个任务,要么执行它,要么拒绝它,不能永远什么都不做。

我们接着在jdk8中,如下两行(2359&2224)打上断点:
2359 U.putOrderedObject(a, j, task); 
2224 rs = lockRunState(); // enter SHUTDOWN phase 

然后再次注释Thread.sleep(1000)。
//	Thread.sleep(1000);

debug我们一开始给出的test case。

然后shutdownPool线程在2224行上挂起,printHelloWorld线程在2359行上挂起。我们先让shutdownPool通过,再让printHelloWorld线程通过。
此时会看到我们的程序没有任何输出,并且printHelloWorld线程永远挂起了。
一个解决方案是在2359行,也就是将任务放入队列之后再次检查下SHUTDOWN状态是否被设置,假如是的话尝试将任务重新删除。


jdk9中的实现与8已经不一样了。详情可见 点击打开链接


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值