Java 同步方法

请考虑以下代码:

public synchronized void onSignalsTimeout(List<SignalSpec> specs) {
    if (specs != null && specs.size() > 0) {
        for (SignalSpec spec : specs) {
            ParsedCANSignal timeoutedSignal = new ParsedCANSignal();
            SignalsProvider.getInstance().setSignal(spec.name, spec.parent.parent.channel, timeoutedSignal);
        }
    }
}

我有一个简单的问题:当线程 1 调用SignalsTimeout 方法时,线程 2 可以访问在该方法中访问的对象吗?

如果“synchronized”仅锁定对此方法的访问或对此方法中使用的所有对象的访问,则找不到任何位置。


答案 1

首先,忘记同步方法。一种所谓的同步方法...

synchronized AnyType foobar(...) {
    doSomething();
}

只不过是写这个的捷径:

AnyType foobar(...) {
    synchronized(this) {
        doSomething();
    }
}

在这两种情况下,该方法都没有什么特别之处。特别的是同步,同步块的作用非常简单。当 JVM 执行以下命令时:

synchronized(foo) {
    doSomething();
}

它首先计算表达式 。结果必须是对象引用。然后,它锁定对象,执行块的主体,然后解锁对象。foosynchronized

但是锁定是什么意思呢?它可能意味着比你想象的要少。它不会阻止其他线程使用该对象。它不会阻止他们访问对象的字段或更新其字段。锁定对象唯一阻止的是,它可以防止其他线程同时锁定同一对象。

如果线程 A 尝试在线程 B 已锁定 foo 时进入(在同一块中,也可能在不同的块中),则线程 A 将被迫等待,直到线程 B 释放锁定。synchronized(foo) {...}synchronized


您可以使用块来保护数据synchronized

假设您的程序具有一些可以处于不同状态的对象集合。假设某些状态有意义,但也有其他状态没有意义 - 无效状态。

假设线程不可能在不临时创建无效状态的情况下将数据从一个有效状态更改为另一个有效状态。

如果将更改状态的代码放在块中,并将每个可以看到状态的代码块放入锁定同一对象的同步块中,则会阻止其他线程看到临时无效状态。synchronized(foo)foo


答案 2

是的,其他线程可以访问该方法中使用的对象;sync 关键字保证一次最多只能执行该方法的代码。

https://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html

  • 首先,在同一对象上两次调用同步方法不可能交错。当一个线程为对象执行同步方法时,调用同一对象块的同步方法的所有其他线程(挂起执行),直到第一个线程完成该对象。
  • 其次,当同步方法退出时,它会自动与同一对象的同步方法的任何后续调用建立“发生之前”关系。这保证了对对象状态的更改对所有线程都可见。请注意,构造函数无法同步 — 将 sync 关键字与构造函数一起使用是语法错误。同步构造函数没有意义,因为只有创建对象的线程才能在构造对象时访问它。

推荐