Java 线程/易失性

发布于 2024-12-01 13:29:36 字数 674 浏览 1 评论 0原文

我有一个线程:

class Foo extends Thread
{
    boolean active = true;

    public void run()
    {
        while(active)
        {
            //do stuff
        }
    }

    public void end()
    {
        active = false;
    }

    public void hibernate()
    {
        synchronized(this)
        {
            wait();
        }
    }
 }

如果另一个线程调用 end()Foo 会立即看到 active 现在是 false ?具体来说,因为 active 不是 易失性,所以我不确定它是否会。我最初创建 end() 作为避免易失性的巧妙方法,但现在我不确定它是否真的会达到我的预期。 此外,如果另一个线程调用 hibernate(),哪个线程将进入睡眠状态?我打算让 Foo 睡觉,所以如果这没有达到我的预期,那么非常欢迎其他建议。

I have a thread:

class Foo extends Thread
{
    boolean active = true;

    public void run()
    {
        while(active)
        {
            //do stuff
        }
    }

    public void end()
    {
        active = false;
    }

    public void hibernate()
    {
        synchronized(this)
        {
            wait();
        }
    }
 }

If another thread calls end(), will Foo immediately see that active is now false? Specifically, because active isn't volatile, I'm not sure that it will. I initially created end() as a clever way of avoiding volatile, but now I'm unsure that it will actually do what I intend.
Additionally, if another thread calls hibernate(), which thread will go to sleep? I'm intending Foo to sleep, so if this doesn't do what I intend, an alternative suggestion would be very welcome.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

沧笙踏歌 2024-12-08 13:29:36

如果另一个线程调用 end(),Foo 会立即看到 active 现在为 false 吗?

不,不会的。或者至少,它不会一直看到它。

如果您希望 run 始终立即看到新值,则分配给变量的线程与读取该变量的线程之间必须存在“紧随其后”的关系。这可以通过以下方式实现:

  • 通过声明 active 易失性,
  • 通过在读写变量的语句周围放置 synchronized 块,
  • 通过使变量成为“原子”类型;例如AtomicBoolean,或者
  • 使用其他一些适当的并发类;请参阅 java.util.concurrent.* 包。

...避免不稳定的巧妙方法...

将变量声明为易失性是确保正确同步的一种方法。事实上,正确的同步会带来性能开销。然而,正确的同步对于应用程序的可靠工作至关重要,避免它并不“聪明”。

(如果没有适当的同步,您的程序可能在大多数时间仍然可以工作,甚至可能总是在某些机器上工作。但是,有时它不会工作,实际行为可能取决于您运行该程序的机器,机器负载是什么,以及其他事情。)

另外,如果另一个线程调用hibernate(),哪个线程会进入睡眠状态?

发出调用的线程将进入睡眠状态。除非其他线程对同一个 Foo 对象执行 notifynotifyAll ,否则它不会唤醒。

如果您只是想让应用程序进入睡眠状态并稍后唤醒,请使用 Thread.sleep。但请注意,以错误的方式使用 sleep 可能会使您的应用程序缓慢且无响应。

If another thread calls end(), will Foo immediately see that active is now false?

No it won't. Or at least, it won't see it all of the time.

If you want run to always see the new value immediately, there has to be a "comes after" relationship between the thread assigning to the variable and the thread reading it. This can be achieved:

  • by declaring active volatile,
  • by putting synchronized blocks around the statements that read and write the variable,
  • by making the variable an "atomic" type; e.g. AtomicBoolean, or
  • by using some other appropriate concurrency class; see the java.util.concurrent.* packages.

... a clever way of avoiding volatile ...

Declaring the variable to be volatile is one way of ensuring proper synchronization. It is a fact that proper synchronization imposes a performance overhead. However, proper synchronization is essential for your application to work reliably, and it is NOT "clever" to avoid it.

(Without proper synchronization, your program will probably still work most of the time, and it might even always work on some machines. However, occasionally it won't work, and the actual behavior is likely to depend on what machine you run the program on, what the machine load is, and other things.)

Additionally, if another thread calls hibernate(), which thread will go to sleep?

The thread that makes the call will go to sleep. And it won't wake up unless some other thread does a notify or notifyAll on the same Foo object.

If you simply want the application to go to sleep and wake up a bit later, use Thread.sleep. But beware that using sleep in the wrong way can make your application slow and unresponsive.

囍孤女 2024-12-08 13:29:36

您的怀疑是正确的:因为 active 不是 易失性,因此不能保证 run() 会看到另一个线程上所做的更改。

一般来说,避免易失性的“聪明”方法几乎总是一个坏主意。事实上,即使是 volatile 也是您不应该使用的东西。大多数时候,坚持使用锁、监视器或更高级的同步机制会更安全。

对于第二个问题,将进入睡眠状态的线程是调用 hibernate() 的线程。该线程将处于休眠状态,直到被中断、遇到虚假唤醒或其他线程调用 Foonotify()/notifyAll() > 实例的监视器。调用Object#wait()而不使用检查正在等待的条件的循环包围它通常是错误的。

您似乎也对 Foo 实例“进入睡眠状态”的想法感到困惑。 Foo 实例不是 Thread (甚至不是 Runnable),并且不会创建自己的线程,因此它的想法去睡觉没有多大意义。您可能想要实现的目标是将调用 Foo#run() 的线程置于睡眠状态。

Your suspicion is correct: because active isn't volatile, there is no guarantee that run() will ever see the change made on another thread.

Generally speaking, “clever” ways of avoiding volatile are almost always a bad idea. In fact, even volatile is something you should prefer not to resort to. Most of the time it's safer to stick to locks, monitors, or higher-level synchronization mechanisms.

For your second question, the thread that will go to sleep is the one that called hibernate(). That thread will sleep until it is interrupted, it experiences a spurious wakeup, or some other thread calls notify()/notifyAll() on the Foo instance's monitor. It is usually a mistake to call Object#wait() without surrounding it with a loop that checks the condition being waited for.

You also seem to be confused about the idea of a Foo instance “going to sleep”. A Foo instance isn't a Thread (or even a Runnable), and doesn't create its own thread, so the idea of it going to sleep doesn't make a lot of sense. What you are probably trying to achieve is putting the thread calling Foo#run() to sleep.

流绪微梦 2024-12-08 13:29:36

关于避免 挥发性 的第一个问题,您应该尝试使用线程中断来通知正在运行的线程停止。

使用 interrupt() 实例另一个线程的方法来中断正在运行的线程。

使用 isInterrupted()< /a> 正在运行的线程中的方法来检查中断。

while(!this.isInterrupted()){
   //do your work here.
}

不确定为什么要扩展 Thread 类。如果在这种情况下实现 Runnable,则应该使用 interrupted 在你的运行方法中检查中断。请阅读 javadocs 以了解此方法的一些注意事项。

Regarding your first question of avoiding volatile , you should try using Thread interruption to signal a running thread to stop.

Use interrupt() instance method from another thread to interrupt running thread.

Use isInterrupted() method in your running thread to check for interruption.

while(!this.isInterrupted()){
   //do your work here.
}

Not sure why you want to extend Thread class. If you implements Runnable in that case you should use interrupted in your run method to check for interruption . Please read javadocs to know about some caveats of this method.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文