Java中的synchronized()/wait()/notifyAll()是做什么用的?

31

可能是重复问题:
Java同步

我正在阅读《Beginning Android Games》一书。

它经常使用 synchronized(),但我不太理解它的作用。我很久没用Java了,也不确定自己是否曾经使用过多线程。

在Canvas示例中,它使用 synchronized(this)。然而在OpenGL ES示例中,它创建了一个名为stateChanged的对象,然后使用 synchronized(stateChanged)。当游戏状态改变时,它调用 stateChanged.wait() 然后是 stateChanged.notifyAll();

一些代码:

    Object stateChanged = new Object();

    //The onPause() looks like this:
    public void onPause()
        {
            synchronized(stateChanged)
            {
                if(isFinishing())
                    state = GLGameState.Finished;
                else
                    state = GLGameState.Paused;

                while(true)
                {
                    try
                    {
                        stateChanged.wait();
                        break;
                    } catch(InterruptedException e)
                    {
                    }
                }
            }
        }
//The onDrawSurface looks like this:
public void onDrawFrame(GL10 gl)
    {
        GLGameState state = null;
        synchronized(stateChanged)
        {
            state = this.state;
        }

        if(state == GLGameState.Running)
        {

        }

        if(state == GLGameState.Paused)
        {
            synchronized(stateChanged)
            {
                this.state = GLGameState.Idle;
                stateChanged.notifyAll();
            }
        }

        if(state == GLGameState.Finished)
        {
            synchronized(stateChanged)
            {
                this.state = GLGameState.Idle;
                stateChanged.notifyAll();
            }
        }
    }

//the onResume() looks like this:
synchronized(stateChanged)
        {
            state = GLGameState.Running;
            startTime = System.nanoTime();
        }

3
是的,我做了... @Joachim Sauver 那个问题并没有涉及到wait()/notify()。 - Tiago Costa
你只是刚刚添加了wait()/notify() - 你应该先好好组织问题,而不是边做边建。 - Charles Goodwin
7
实际上,当我创建这篇帖子时,我甚至没有在标题中指定 synchronized(),但是某些“版主”将标题更改为“Java 中的 synchronized() 是什么?”...好吧,我们能否停止这个讨论,因为它并没有帮助我解决问题... - Tiago Costa
正如@Charles Goodwin所怀疑的那样,您接受的Java教程答案也出现在谷歌搜索“java synchronized”的第三项中。这不是一个支持问题,而是一个基本的Java概念。 - cdhabecker
13
在Google搜索某个主题时,第一个链接被SO(Stack Overflow的缩写)标记为重复内容,但没有提及所称答案的参考文献,这真的很让人失望。至少有一些人给了答案,对此我很感激。 - Allen Edwards
显示剩余2条评论
7个回答

51

synchronized关键字用于保证变量或者方法的线程安全性。如果您想将一个变量放在同步代码块中用作如下:

synchronized(myVar) {
    // Logic involing myVar
}

在 synchronized 块内执行逻辑时,任何试图从另一个线程修改 myVar 的尝试都将等待该块执行完毕。这确保了进入块的值将在该块的生命周期内保持不变。


这样说Java版本的Mutex是正确的吗? - Siavash
1
@Siavash 来自 .NET?是的。 - Jason Robinson

31

这个Java教程或许可以帮助您了解在对象上使用 synchronized 的作用。

当调用object.wait()时,它将释放对该对象的锁定(这是在您说synchronized(object)时发生的),并冻结线程。然后线程等待直到有另一个线程调用object.notify()object.notifyAll()。一旦其中之一被调用,它将允许由于object.wait()而停止的任何线程继续。这并不意味着调用object.notify()object.notifyAll()的线程将冻结并将控制权传递给等待线程,只是这些等待线程现在能够继续执行,而之前它们无法继续执行。


这很有道理...所以使用wait()会停止将暂停应用程序的主线程,使OpenGL ES停止。然后,在OpenGL ES停止之后,渲染器线程调用notifyAll(),使主线程完成暂停应用程序。 - Tiago Costa

10

当像这样使用时:

private synchronized void someMehtod()

以下是效果:

1. 首先,使用相同对象的两个同步方法调用不可能交错执行。当一个线程执行对象的同步方法时,所有其他调用相同对象的同步方法的线程都会被阻塞(暂停执行),直到第一个线程完成为止。

2. 其次,在同步方法退出时,它会自动与后续对同一对象的同步方法调用建立 happens-before 关系。这保证了对对象状态的更改对所有线程都可见。

(摘自此处

当您使用代码的同步块时,也会获得类似的效果:

private void someMethod() {
  // some actions...

  synchronized(this) {
    // code here has synchronized access
  }

  // more actions...
}

这里所解释的。


1
好的。那么wait()和notify()方法呢?它们如何干涉线程...(我已经在原问题中添加了一些我的代码) - Tiago Costa

4
Java(Android基于此)可以在多个线程下运行,可以利用多个CPU核心。多线程意味着您可以让Java在 exact 相同的时刻执行两个进程。如果您有一段代码或方法需要确保只能由一个线程同时操作,那么您需要同步该块代码。这里是Oracle官方的Java解释

重要的是要知道,使用 synchronized 会涉及处理器/IO成本,只有在需要时才应使用它。还要研究哪些Java类/方法是线程安全的。例如,++增量运算符不能保证线程安全,而您可以轻松创建一块同步代码块,使用 += 1 进行增量。

1

只有一个线程可以在给定对象的同步块内活动。调用wait会放弃这个权利并停止当前线程,直到有人调用notify(all)()。然后,不活动的线程开始再次想要在同步块中运行,但与所有其他想要它的线程一样被处理。只有一个某种方式选择的(程序员不能影响也不能依赖于哪一个)实际上到达那里。


1

Java中的synchronized关键字有两个作用。

第一个意义是所谓的临界区,即只能由一个线程同时访问的代码部分。你传递给synchronized的对象允许某种命名:如果一个代码在synchronized(a)中运行,它不能访问另一个块,该块位于synchronized(a)中,但可以访问synchronized(b)中的代码块。

另一个问题是线程间通信。线程可以等待其他线程通知它。等待和通知都必须写在synchronized块中。

这只是一个非常简短的描述。我建议您搜索一些多线程教程并阅读它们。


0
关键字synchronized,连同wait和notify操作一起形成了一个非阻塞条件监视器,这是一个有用的构造,用于协调多个线程。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接