做一个小练习记录一个阻塞方法引起的任务无法结束。
场景如下:一个生成质素的类,多个线程调用这个类生成一系列质素。
质素生成类:
public class PrimeGeneratorForeverRun implements Callable<BlockingQueue<BigInteger>> { private final BlockingQueue<BigInteger> primes = new ArrayBlockingQueue<BigInteger>( 5);//使用阻塞队列来存储已经生成的质素 private volatile boolean cancelled = false;//退出条件 private static BigInteger b = BigInteger.ONE; @Override public BlockingQueue<BigInteger> call() throws Exception { System.out.println("Begin Time: " + new Date()); while (!cancelled) { TimeUnit.SECONDS.sleep(1); synchronized (this) { b = b.nextProbablePrime(); primes.put(b); } } System.out.println("End Time: " + new Date()); return primes; } public void cancel() {//当外部调用这个方法时,将时call中的while循环退出 cancelled = true; } }
测试类:
public class PrimeGeneratorForeverRunTest { /** * @param args */ public static void main(String[] args) { PrimeGeneratorForeverRun primeGenerator = new PrimeGeneratorForeverRun();//定义一个单例的质素生成器 ExecutorService service = Executors.newFixedThreadPool(2);//定义线程池的大小为2 Future<BlockingQueue<BigInteger>> result1 = service.submit(primeGenerator);//提交任务 Future<BlockingQueue<BigInteger>> result2 = service.submit(primeGenerator); try { TimeUnit.SECONDS.sleep(10);//守护线程沉睡10秒,目的是向在10秒内让任务1,2不停的生成质素 } catch (Exception e) { e.printStackTrace(); } finally { primeGenerator.cancel();//停止生成质素 service.shutdown();//关闭线程池,这里使用平滑关闭,目的是不强制退出任何线程 try { BlockingQueue<BigInteger> list = result1.get();//获取第一个任务生成的所有质素 for (BigInteger bigInteger : list) { System.out.print(bigInteger + ","); } System.out.println(); list = result2.get(); for (BigInteger bigInteger : list) {//获取第二个任务生成的所有质素 System.out.print(bigInteger + ","); } } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } } }
运行测试类,程序死掉了,一直在运行,查看JVM线程栈信息:
Full thread dump Java HotSpot(TM) Server VM (17.0-b16 mixed mode): "Attach Listener" daemon prio=10 tid=0x08e7ec00 nid=0x4392 waiting on condition [0x00000000] java.lang.Thread.State: RUNNABLE Locked ownable synchronizers: - None "pool-1-thread-2" prio=10 tid=0x08e70400 nid=0x4369 waiting on condition [0x7b629000] java.lang.Thread.State: WAITING (parking) at sun.misc.Unsafe.park(Native Method) - parking to wait for <0xa285ddf0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:158) at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:1987) at java.util.concurrent.ArrayBlockingQueue.put(ArrayBlockingQueue.java:252) at org.victorzhzh.concurrency.PrimeGeneratorForeverRun.call(PrimeGeneratorForeverRun.java:24) - locked <0xa285db78> (a org.victorzhzh.concurrency.PrimeGeneratorForeverRun) at org.victorzhzh.concurrency.PrimeGeneratorForeverRun.call(PrimeGeneratorForeverRun.java:1) at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303) at java.util.concurrent.FutureTask.run(FutureTask.java:138) at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908) at java.lang.Thread.run(Thread.java:619) Locked ownable synchronizers: - <0xa285e710> (a java.util.concurrent.locks.ReentrantLock$NonfairSync) "pool-1-thread-1" prio=10 tid=0x08e6cc00 nid=0x4368 waiting for monitor entry [0x7b87a000] java.lang.Thread.State: BLOCKED (on object monitor) at org.victorzhzh.concurrency.PrimeGeneratorForeverRun.call(PrimeGeneratorForeverRun.java:23) - waiting to lock <0xa285db78> (a org.victorzhzh.concurrency.PrimeGeneratorForeverRun) at org.victorzhzh.concurrency.PrimeGeneratorForeverRun.call(PrimeGeneratorForeverRun.java:1) at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303) at java.util.concurrent.FutureTask.run(FutureTask.java:138) at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908) at java.lang.Thread.run(Thread.java:619) Locked ownable synchronizers: - <0xa285e520> (a java.util.concurrent.locks.ReentrantLock$NonfairSync) "Low Memory Detector" daemon prio=10 tid=0x08e57400 nid=0x4366 runnable [0x00000000] java.lang.Thread.State: RUNNABLE Locked ownable synchronizers: - None "CompilerThread1" daemon prio=10 tid=0x08e54000 nid=0x4365 waiting on condition [0x00000000] java.lang.Thread.State: RUNNABLE Locked ownable synchronizers: - None "CompilerThread0" daemon prio=10 tid=0x08e52000 nid=0x4364 waiting on condition [0x00000000] java.lang.Thread.State: RUNNABLE Locked ownable synchronizers: - None "Signal Dispatcher" daemon prio=10 tid=0x08e50400 nid=0x4363 runnable [0x00000000] java.lang.Thread.State: RUNNABLE Locked ownable synchronizers: - None "Finalizer" daemon prio=10 tid=0x08e3dc00 nid=0x4362 in Object.wait() [0x7be83000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0xa2820b10> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:118) - locked <0xa2820b10> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:134) at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:159) Locked ownable synchronizers: - None "Reference Handler" daemon prio=10 tid=0x08e3c400 nid=0x4361 in Object.wait() [0x7bed4000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0xa2820a18> (a java.lang.ref.Reference$Lock) at java.lang.Object.wait(Object.java:485) at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:116) - locked <0xa2820a18> (a java.lang.ref.Reference$Lock) Locked ownable synchronizers: - None "main" prio=10 tid=0x08db0400 nid=0x435d waiting on condition [0xb6b1a000] java.lang.Thread.State: WAITING (parking) at sun.misc.Unsafe.park(Native Method) - parking to wait for <0xa285e4c0> (a java.util.concurrent.FutureTask$Sync) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:158) at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:811) at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedInterruptibly(AbstractQueuedSynchronizer.java:969) at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(AbstractQueuedSynchronizer.java:1281) at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:218) at java.util.concurrent.FutureTask.get(FutureTask.java:83) at org.victorzhzh.concurrency.PrimeGeneratorForeverRunTest.main(PrimeGeneratorForeverRunTest.java:30) Locked ownable synchronizers: - None "VM Thread" prio=10 tid=0x08e39800 nid=0x4360 runnable "GC task thread#0 (ParallelGC)" prio=10 tid=0x08db7800 nid=0x435e runnable "GC task thread#1 (ParallelGC)" prio=10 tid=0x08db9000 nid=0x435f runnable "VM Periodic Task Thread" prio=10 tid=0x08e59000 nid=0x4367 waiting on condition JNI global references: 855
主要看,如下信息:
"pool-1-thread-2" prio=10 tid=0x08e70400 nid=0x4369 waiting on condition [0x7b629000] java.lang.Thread.State: WAITING (parking) at sun.misc.Unsafe.park(Native Method) - parking to wait for <0xa285ddf0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:158) at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:1987) at java.util.concurrent.ArrayBlockingQueue.put(ArrayBlockingQueue.java:252) at org.victorzhzh.concurrency.PrimeGeneratorForeverRun.call(PrimeGeneratorForeverRun.java:24) - locked <0xa285db78> (a org.victorzhzh.concurrency.PrimeGeneratorForeverRun) at org.victorzhzh.concurrency.PrimeGeneratorForeverRun.call(PrimeGeneratorForeverRun.java:1) at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303) at java.util.concurrent.FutureTask.run(FutureTask.java:138) at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908) at java.lang.Thread.run(Thread.java:619) Locked ownable synchronizers: - <0xa285e710> (a java.util.concurrent.locks.ReentrantLock$NonfairSync) "pool-1-thread-1" prio=10 tid=0x08e6cc00 nid=0x4368 waiting for monitor entry [0x7b87a000] java.lang.Thread.State: BLOCKED (on object monitor) at org.victorzhzh.concurrency.PrimeGeneratorForeverRun.call(PrimeGeneratorForeverRun.java:23) - waiting to lock <0xa285db78> (a org.victorzhzh.concurrency.PrimeGeneratorForeverRun) at org.victorzhzh.concurrency.PrimeGeneratorForeverRun.call(PrimeGeneratorForeverRun.java:1) at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303) at java.util.concurrent.FutureTask.run(FutureTask.java:138) at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908) at java.lang.Thread.run(Thread.java:619) Locked ownable synchronizers: - <0xa285e520> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
由于阻塞队列满了,所以pool-1-thread-2进入了等待状态,而由于pool-1-thread-2锁住了PrimeGeneratorForeverRun对象,所以pool-1-thread-1进入了阻塞状态,因此即使这时我们调用了PrimeGeneratorForeverRun.cancel()方法,也无法停止生成质素的循环,因为线程一个处于等待状态,一个处于阻塞状态,都不能取校验cancelled标志位,所以线程只能保持源状态继续,而守护线程中我们用的是shutdown,只要有线程在运行那么线程池就不会关闭,因此程序将一直运行。
解决方法很容易了,这里就不再列出。