【问题标题】:Weird things happen when I try to kill a Thread in java?当我试图在 java 中杀死一个线程时会发生奇怪的事情?
【发布时间】:2021-04-24 19:58:51
【问题描述】:

我有这个“串行”类,它扩展了与串行端口一起工作的线程。在 run() 方法中,我首先打开端口,设置所有串行参数并添加一个 SerialPortEventListener。然后我只是在 go 是私有的(所以只有类可以看到它)布尔实例设置为 true 的地方放了一段时间(go)。我有一个 kill 方法,它不仅关闭端口并删除 SerialEventListener,而且还将“go”设置为 false,因此线程应该停止。现在,事情开始变得奇怪了:如果我在 while(go) 中放了一个 System.out.println(something),当我调用 kill() 方法时,Thred 停止,但如果我不这样做,它仍然活着.您对它为什么会这样以及如何解决它有什么想法吗?

while(go) {
            System.out.println("in");
        }
public void kill(){
        go = false;
        try {
            this.serialPort.removeEventListener();
            this.serialPort.closePort();
        } catch (SerialPortException e) {
            e.printStackTrace();
        }
    }

【问题讨论】:

  • 你应该永远在没有保护的情况下从两个线程访问同一个变量。正如您已经发现的那样,这是灾难的根源。你需要某种形式的synchronization
  • 我该怎么做?我可以将同步词添加到 kill() 方法吗?
  • 你需要同步all对变量的访问,而不仅仅是kill中的修改。当您在run 中访问它时,它还需要同步(使用块,不是整个方法;如果您同步整个run 方法,您将永远无法调用@987654328 @)
  • 感谢您的宝贵时间,能否请您提供一个示例代码?如您所知,我只在 while(go)(在方法 run() 中)和 kill() 中访问变量“go”
  • The section on sleep and yield 在 Java 语言规范中解释得很好。

标签: java multithreading


【解决方案1】:

除非 go 被声明为 volatile,否则无法保证 while 循环(或其他任何内容)会读取当前值。

声明为

volatile boolean go;

那么它应该可以工作(除非还有其他尚未发布的事情发生)。这样做并不能减轻正确同步的要求。

【讨论】:

  • 好东西:它工作得很好。坏事:我不知道“volatile”是什么意思,也不知道如何进行同步,请您帮帮我吗?
  • 不幸的是,这是一个涉及的主题,会花费太多时间。但是您可以查看Java Tutorials - concurrency 的这一部分,该部分讨论了如何正确同步线程。关键字volatile 包含在atomic access 部分中。网络上还有其他来源。当出现具体问题时,您总是可以回到 SO 并就此提出问题。并且你也可以搜索SO搜索synchronization这个话题,因为之前已经介绍过很多次了。
  • @DavidePasero 坦率地说,如果您不了解volatile(和Atomic… 类),您应该进行线程/并发编程。阅读本网站上的 Oracle 教程、搜索和学习帖子,最重要的是阅读 Brian Goetz 等人的经典书籍 Java Concurrency in Practice
【解决方案2】:

我不会争论你实现这些线程的方式。然而,看看具体的场景,你有一个Serial 类,只要变量gotrue,它就会保持活动状态并完成它的工作。一旦调用kill 方法,另一个线程(即主线程)也可以更改相同的变量。
正如@WJS 所建议的,使事情正常工作的最佳方法是将变量go 声明为“volatile”。你应该有这样的东西:

public class Serial extends Thread {
   ...
   private volatile boolean go;
   ...

   public void kill(){
      ...
   }
   ...
}

基本上,通过这种方式,JVM 确保不同的线程读取变量的一致值,并且实际上这就是在像您这样的多线程场景中发生的情况。

另一种方法是将变量 go 定义为java.util.concurrent.atomic.AtomicBoolean,而不是将其定义为简单的原语boolean。这在这种情况下也很有效,因为通过这种方式,JVM 保证变量值的每次更改都是原子操作,这意味着将其设置为“at one”,并且您将始终避免任何可能的竞争条件。 在这种情况下,您的代码应该是这样的:

import java.util.concurrent.atomic.AtomicBoolean;
...
public class Serial extends Thread {
    private AtomicBoolean go = new AtomicBoolean(true);
    ...

    public void loop() {
        ...
        while(go.get()) {
        
        }
    }
 
    public kill() {
       atomicBoolean.set(false);
       ...
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-22
    • 1970-01-01
    • 2019-07-16
    相关资源
    最近更新 更多