为什么可运行任务不能被AtomicBoolean标志中断?

时间:2019-02-16 15:14:48

标签: java multithreading concurrency

我有下一个任务

public class QuittableTask implements Runnable {
  final int id;

  public QuittableTask(int id) {
    this.id = id;
  }

  private AtomicBoolean running = new AtomicBoolean(true);

  public void quit() {
  System.out.println("Quite " + Thread.currentThread().getName());
     running.set(false);
  }

  @Override
   public void run() {
    while (running.get()) {        
        System.out.println(Thread.currentThread().getName());
        new Nap(0.1);
    }
    System.out.print(id + " ");  
   }

我使用运行许多任务

public class QuittingTasks {
  public static final int COUNT = 1500;

public static void main(String[] args) throws InterruptedException {
    ExecutorService es = Executors.newCachedThreadPool();
    List<QuittableTask> tasks =
            IntStream.range(1, COUNT)
                    .mapToObj(QuittableTask::new)
                    .peek(es::execute)
                    .collect(Collectors.toList());
    Thread.currentThread().sleep(1000);
    tasks.forEach(QuittableTask::quit);
    es.shutdown();
}

当我使用 COUNT <10000 时,一切正常。所有线程都将终止。编程在2秒内完成。

但是当我使用 COUNT> 100001 方法时,无法达到相当的方法,并且我的程序将进入无限循环。我不明白这种行为。有人可以向我解释吗?

2 个答案:

答案 0 :(得分:3)

如果仔细查看日志(在代码上添加一些日志),则可以在程序生成OutOfMemoryError时看到。该错误如下所示:

java.lang.OutOfMemoryError: unable to create new native thread
    at java.lang.Thread.start0(Native Method)
    at java.lang.Thread.start(Thread.java:717)
    at java.util.concurrent.ThreadPoolExecutor.addWorker(ThreadPoolExecutor.java:957)
    at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1378)
    at java.util.stream.ReferencePipeline$11$1.accept(ReferencePipeline.java:372)
    at java.util.stream.IntPipeline$4$1.accept(IntPipeline.java:250)
    at java.util.stream.Streams$RangeIntSpliterator.forEachRemaining(Streams.java:110)
    at java.util.Spliterator$OfInt.forEachRemaining(Spliterator.java:693)
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
    at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
    at com.java8.demo.Main.main(Main.java:58)

这意味着当您尝试在所有任务上调用quit时,您将获得OutOfMemory,但是由于发生错误而永远不会执行该错误,因为布尔标志永远不会设置为false。其他线程继续运行。

您会注意到内存不足是因为VM无法创建本机线程。现在您正在使用Executors.newCachedThreadPool();。因此您的线程池最大大小可以为Integer.MAX_VALUE,但在达到该值之前,您的VM无法创建新线程。我已将执行程序更改为Fix ExecutorService es = Executors.newFixedThreadPool(8);到FixTheradPool。当您创建更高的COUNT值时,它可以正常工作,因为它将仅创建8个线程。

我放置了一个try-catch块来跟踪如下所示的异常。另外,我通过调用System#exit在控制台上跟踪日志来强制退出VM。

try {
    ExecutorService es = Executors.newCachedThreadPool();
    List<QuittableTask> tasks = IntStream.range(1, COUNT).mapToObj(QuittableTask::new).peek(es::execute)
            .collect(Collectors.toList());
    Thread.currentThread().sleep(1000);
    tasks.forEach(QuittableTask::quit);
    es.shutdown();
} catch (Throwable e) {
    System.out.println("Error" + e.getMessage());
    e.printStackTrace();
    System.exit(0);
}

答案 1 :(得分:2)

要添加到@AmitBera的答案中-Executors.newCachedThreadPool()创建了一个几乎不受限制的线程池(请参阅the API doc中“排队”部分的说明)。因此,每个新的QuittingTask实例都会移交给新线程并立即执行。

在某些时候,QuittingTask实例和新线程实例(尤其是新线程实例)的数量会耗尽内存。这个“点”对JVM的内存配置很敏感。您碰巧获得了10001。运行此程序时,我用了4073。