简体   繁体   English

"由于并发,Java 代码在几秒钟后退出"

[英]Java code exits after some seconds due to concurrency

I am writing the same code on tutorial.我在教程上写了同样的代码。 But in tutorial the program never exits, my in my computer it exits after 4 seconds.但是在教程中程序永远不会退出,我在我的电脑中它会在 4 秒后退出。 Why?为什么? tutorial with exact time where this code is shown: https:\/\/youtu.be\/vzBw1LPupnA?t=169<\/a>带有显示此代码的确切时间的教程: https<\/a> :\/\/youtu.be\/vzBw1LPupnA?t=169

public class Main {
    private static boolean stopRequested;

    public static void main(String[] args) throws InterruptedException {
        Thread backgroundThread = new Thread(() -> {
            int i = 0;
            while (!stopRequested) {
                i++;
                System.out.println("i = " + i);
            }
        });
        backgroundThread.start();
        TimeUnit.SECONDS.sleep(1);
        stopRequested = true;
    }
}

The reason that you are seeing different behavior on your machine and in the video is because the program has unspecified behavior.您在机器上和视频中看到不同行为的原因是程序具有未指定的行为。

You have two threads accessing and updating a shared variable without taking the necessary steps that will guarantee that changes made by one thread are visible to the other.您有两个线程访问和更新一个共享变量,而没有采取必要的步骤来保证一个线程所做的更改对另一个线程可见。 What happens in that case is not specified.没有具体说明在这种情况下会发生什么。 In some cases (eg on some platforms) the changes will be visible, either immediately or within a short time.在某些情况下(例如在某些平台上),更改将立即或在短时间内可见。 On others, it may never<\/em> be visible.在其他人身上,它可能永远<\/em>不可见。

In technical terms, there must be a happens-before<\/em> relationship between the write by on thread and the subsequent read by the other thread.用技术术语来说,线程上的写入与另一个线程的后续读取之间必须存在发生前的<\/em>关系。 This can be provided by both threads synchronizing on the same mutex or lock, by using a volatile<\/code> variable, and other things.这可以通过在同一个互斥锁或锁上同步的两个线程、通过使用volatile<\/code>变量和其他方式来提供。 But this code doesn't do those things.但是这段代码并没有做这些事情。

For more details, read about the Java Memory Model.有关更多详细信息,请阅读 Java 内存模型。


The above is sufficient<\/em> to explain the difference, but there may be a more direct explanation.以上足以<\/em>解释差异,但可能有更直接的解释。

In practice, something like a System.out.println<\/code> can lead to changes in the visibility.在实践中,像System.out.println<\/code>这样的东西会导致可见性的变化。 Underneath the covers, the println<\/code> call will typically result in synchronization on the output stream's buffers.在幕后, println<\/code>调用通常会导致输出流缓冲区的同步。 That can result in a serendipitous<\/em> happens-before that is sufficient to guarantee visibility.这可能会导致意外<\/em>发生——在这足以保证可见性之前。 But this behavior is not specified, so you should not rely on it.但是没有指定这种行为,所以你不应该依赖它。

At any rate, adding trace statements can change the behavior of multi-threaded coded.无论如何,添加跟踪语句可以改变多线程编码的行为。 And the fact that you (apparently) added them in your version is a second possible explanation for the difference.您(显然)在您的版本中添加了它们的事实是对差异的第二种可能的解释。


The bottom line here is that a program with a memory visibility flaw is broken, but you may not be able to demonstrate<\/em> that it is broken.这里的底线是具有内存可见性缺陷的程序已损坏,但您可能无法证明<\/em>它已损坏。

"

As the excellent Answer by Stephen C says, your code is not thread-safe .正如Stephen C 的出色回答所说,您的代码不是线程安全的

Establishing an AtomicBoolean early on addresses the visibility problem explained in that other Answer.尽早建立AtomicBoolean可以解决其他答案中解释的可见性问题。 This class is a thread-safe wrapper around its payload boolean value.此类是围绕其有效负载boolean值的线程安全包装器。

The volatile keyword is another solution. volatile关键字是另一种解决方案。 But I find the Atomic… classes simpler and more obvious.但我发现Atomic…类更简单、更明显。

Also, in modern Java we rarely need to address the Thread class directly.此外,在现代 Java 中,我们很少需要直接处理Thread类。 Instead, use the Executors framework.相反,请使用 Executors 框架。 Define your task as a Runnable or Callable , and submit to an executor service.将您的任务定义为RunnableCallable ,并提交给执行器服务。

Something like this untested code.像这个未经测试的代码。

public class Main {
    private static final AtomicBoolean stopRequested = new AtomicBoolean( false ) ;

    public static void main(String[] args) throws InterruptedException {
        Runnable task = () -> {
            int i = 0;
            while ( ! stopRequested.get() ) {
                i++;
                System.out.println("i = " + i);
                TimeUnit.MILLISECONDS.sleep(100); // Don’t spin too fast.
            }
        };
        ExecutorService es = Executors.newSingleThreadedExecutorService() ;
        es.submit( task ) ;
        TimeUnit.SECONDS.sleep(1);
        stopRequested.set( true ) ;
        TimeUnit.SECONDS.sleep(1);

   // Shut down here executor service. Boilerplate taken from Javadoc.
   es.shutdown(); // Disable new tasks from being submitted
   try {
     // Wait a while for existing tasks to terminate
     if (!es.awaitTermination(60, TimeUnit.SECONDS)) {
       es.shutdownNow(); // Cancel currently executing tasks
       // Wait a while for tasks to respond to being cancelled
       if (!es.awaitTermination(60, TimeUnit.SECONDS))
           System.err.println("Executor service did not terminate");
     }
   } catch (InterruptedException ex) {
     // (Re-)Cancel if current thread also interrupted
     es.shutdownNow();
     // Preserve interrupt status
     Thread.currentThread().interrupt();
   }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM