Java中的synchronized构造是否在内部(以某种方式)使用硬件原语CAS操作?

Car*_*ira 7 java concurrency synchronization scala java-memory-model

我有一个很难理解什么是硬件支持synchronized声明和相关的notify(),notifyAll()wait()方法,目前所有的Java对象.

我已阅读并知道如何使用此构造,但我一直认为它们直接映射到硬件基元.

当我进一步深入研究有关并发性的书籍时,我只阅读了由硬件直接提供的比较和交换(CAS)操作.

似乎这些构造是由JVM本身创建/维护的.如果我的读数正确,则每个对象都包含一些状态,其中包含有关访问它的线程的信息.这用于定义该对象的监视器并协调多个线程对该对象的访问.
但如果是这种情况,这个状态本身如何通过并发访问来管理?它一定要管理,对吗?是CAS吗?

如果是CAS,那意味着只有一种真正的同步形式CAS.所有其他都是衍生品.那么,为什么是这台显示器构建与关联synchronized,notify(),notifyAll(),wait()开发给出的方法是,原子变量(即CAS)是在性能方面更好,也无等待?

我知道用户类的原子变量只出现在Java 5.0之后,但在此之前,Java已经监视/内部锁.他们是如何实施的?

pio*_*cki 4

Settle in kids this is going to be a long one.

First lest discuss CAS (Compare And Swap) this is not a synchronization mechanism. It is a atomic operation that allows us to update a value in main memory, simultaneity testing if that value has not changed (or is what we expect it to be). There is no locking involved. Although they are used by some synchronization primitives (semaphores, mutexes). Lest's take a look on the following example:

              a = 1;
--------------------------------
     Thread 1   |  Thread 2 
    b = 1 + a   |   b = 2 + a
 cas(*a, 1, b ) | cas(*a, 1, b )
Run Code Online (Sandbox Code Playgroud)

Now one of the CAS-es will fail, and what I mean by that is that it will return false. The other will return true and the value that pointer *a represents will be updated with new value. If we didn't use CAS but instead just updated the value, like this:

              a = 1;
--------------------------------
     Thread 1   |  Thread 2 
    b = 1 + a   |   b = 2 + a
      a = b     |     a = b
Run Code Online (Sandbox Code Playgroud)

At the end of this computation the a could be 2 or 3 and both threads would complete happily not knowing what value was saved in a. This is what is called a data race and CAS is a way to solved that.

The existence of CAS enable us to write some lock-free algorithms (no synchronization needed) like collections in the java.util.concurrent package, that do not need to be synchronized, to be accessed concurrently.

现在我提到CAS是用来实现同步的。这就是为什么获取锁和执行 CAS 的成本几乎相同(如果没有争用!!!),并且在该发送中您可以获得同步关键字的硬件支持。

synchronized(this){ 
     n = n + 1; 
}

AtomicLong al = new AtomicLong();
al.updateAndGet( n -> n + 1)
Run Code Online (Sandbox Code Playgroud)

使用 CAS 与同步时可能会受到性能影响,因为当 CAS 失败时,您可以重试,而使用同步可能会导致线程进入操作系统休眠状态。进入上下文切换的兔子洞(这可能会也可能不会发生:)取决于操作系统)。

现在对于notify(), notifyAll() and wait(). 直接调用属于操作系统一部分的线程调度程序。调度程序有两个队列:等待队列运行队列。当您调用线程上的等待时,该线程将被放置在 wq 中并坐在那里,直到它收到通知并放置在 rq 中以便尽快执行。

在Java中有两种基本的线程同步,一种通过(wait()、notify())称为合作,另一种通过锁称为互斥(mutex)。这通常是平行轨道同时进行思考。

现在我不知道Java 5之前的同步是如何完成的。但是现在你有两种使用对象进行同步的方法(其中一种可能是旧的,另一种可能是新的)。

  1. 偏向锁。线程 ID 被放入对象头中,然后当同一个特定线程想要锁定时,解锁该对象,该操作不会花费我们任何费用。这就是为什么如果我们的应用程序有很多无争用的锁,这可以给我们带来显着的性能提升。因为我们可以避免第二条路:

  2. (这可能是旧的)使用monitorenter/monitorexit. 这是字节码指令。它们被放置在synchronize {...}语句的入口和出口处。这就是对象标识变得相关的地方。因为它成为锁信息的一部分。

好的,就这样吧。我知道我没有完全回答这个问题。这个话题是如此复杂,如此困难。《Java语言规范》中的第17章标题为:“Java内存模型”可能是唯一一个普通程序员无法阅读的章节(也许动态调度也属于这一类:))。我希望至少你能够在谷歌上搜索到正确的单词。

几个链接: https://www.artima.com/insidejvm/ed2/threadsynchP.html(monitorenter/monitorexit,解释)

https://www.ibm.com/developerworks/library/j-jtp10185/index.html(如何在jvm内部优化锁)