首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Java中的同步构造是否在内部(并以某种方式)使用硬件原语CAS操作?

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

Stack Overflow用户
提问于 2018-11-20 20:20:33
回答 1查看 291关注 0票数 7

我很难理解硬件支持用于synchronized语句和相关的notify()notifyAll()wait()方法,这些方法存在于每个java对象上。

我已经阅读并知道如何使用这些构造,但我总是假设它们被直接映射到硬件原语。

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

这个构造似乎是由JVM本身创建/维护的。如果我的读取是正确的,那么每个对象都包含一些状态,其中包含有关访问它的线程的信息。它用于定义该对象的监视器,并协调对该对象的多个线程的访问。

但是如果是这样的话,这个状态本身是如何通过并发访问来管理的呢?这肯定是必须管理的,对吧?和CAS在一起吗?

如果是CAS,这意味着只有一种真正的同步形式,CAS。其他的都是衍生产品。那么,为什么这个监视器构造与相关的synchronizednotify()notifyAll()wait()方法相关联,因为原子变量(即CAS)在性能上更好,并且没有等待?

我知道用户类的原子变量只出现在Java5.0之后,但在此之前,这种监视/内部锁已经出现了。它们是如何实施的?

EN

回答 1

Stack Overflow用户

发布于 2018-11-21 14:04:58

在孩子里安顿下来,这将是一个漫长的过程。

首先,为了避免讨论CAS (Compare Aa Swap),这不是一种同步机制。这是一个原子操作,允许我们更新主内存中的值,同时测试该值是否没有改变(或者是我们预期的那样)。不涉及锁定。虽然它们被一些同步原语(信号量、互斥量)所使用。请看一下下面的示例:

代码语言:javascript
复制
              a = 1;
--------------------------------
     Thread 1   |  Thread 2 
    b = 1 + a   |   b = 2 + a
 cas(*a, 1, b ) | cas(*a, 1, b )

现在其中一个CAS-es将失败,我的意思是它将返回假。另一个将返回true,指针*a所代表的值将用新值更新。如果我们不使用CAS,而只是更新了值,如下所示:

代码语言:javascript
复制
              a = 1;
--------------------------------
     Thread 1   |  Thread 2 
    b = 1 + a   |   b = 2 + a
      a = b     |     a = b

在计算结束时,a可以是2或3,两个线程都会很高兴地完成,不知道在a中保存了什么值。这就是所谓的数据竞赛,CAS就是解决这个问题的一种方法。

CAS的存在使我们能够编写一些无锁算法(不需要同步),比如java.util.concurrent包中的集合,这些算法不需要同步,可以并发访问。

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

代码语言:javascript
复制
synchronized(this){ 
     n = n + 1; 
}

AtomicLong al = new AtomicLong();
al.updateAndGet( n -> n + 1)

在使用CAS vs synchronize时,性能可能会受到影响,因为当CAS失败时,您可以重试,而使用synchronize可能会导致线程进入睡眠状态。进入上下文开关的兔子洞(可能发生或不发生:)取决于操作系统。

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

在Java中,有两个基本的线程同步--一个是via (wait() ),notify()称为协作,另一个是称为互斥 (mutex)的通入锁。而这通常是平行的轨道,要立即思考。

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

  1. 偏锁。线程id被放入对象标头中,然后当相同的特定线程想要锁定时,解锁操作不需要花费我们的任何代价。这就是为什么如果我们的应用程序有很多没有竞争的锁,这可以给我们带来显著的性能提升。因为我们可以避免第二条路径:
  2. (这可能是旧的)使用monitorenter/monitorexit。这是字节码指令。它放在synchronize {...}语句的入口和出口处。这就是对象标识变得相关的地方。因为它成为锁信息的一部分。

好的,就这样。我知道我没有完全回答这个问题。这门课既复杂又难。"Java语言规范“中题为:"Java内存模型”的第17章可能是普通程序员唯一不能阅读的章节(可能动态分派也属于该类别:)。我的希望是,至少你能谷歌正确的词。

几个链接:https://www.artima.com/insidejvm/ed2/threadsynchP.html (监控中心/监视器,解释)

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

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/53400931

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档