美文网首页
java内存模型对并发问题的解决方案

java内存模型对并发问题的解决方案

作者: 兮兮码字的地方 | 来源:发表于2019-07-28 14:44 被阅读0次

我们已经知道,由于为了优化性能,现有的计算机体系设计使得并发程序可能会出现三种问题:可见性问题,原子性问题,有序性问题

而java为了解决可见性问题和有序性问题,它的内存模型定义了很多规则来对编译器和处理器进行[限制]。同时面相开发人员公开了一些开启这些[限制]的方法来规避一些并发问题。

简单来说,就是禁止使用cpu缓存和编译优化的方法,来解决可见性问题和有序性问题

具体,这些方法包括 volatile、synchronized 和 final 三个关键字,以及六项 Happens-Before 规则。


Happens-Before 规则

(1)程序次序规则

在同一个线程内,按照执行顺序,前面的任何操作Happens-Before于后面的任何操作。

(2) volatile 变量规则

对一个 volatile 变量的写操作, Happens-Before 于后续对这个 volatile 变量的读操作。

(3)传递性

如果 A Happens-Before B,且 B Happens-Before C,那么 A Happens-Before C。

我们以下面这段代码为例对前三点做解释。

class VolatileExample {
  int x = 0;
  volatile boolean v = false;
  public void writer() {
    x = 42;
    v = true;
  }
  public void reader() {
    if (v == true) {
      // 这里 x 会是多少呢?
    }
  }
}
java内存模型对并发问题的解决方案

根据规则(1),线程A中写变量 “x = 42;” Happens-Before 于写变量 “v = true;”

根据规则(2),线程A中写变量“v=true” Happens-Before 线程B读变量 “v==true”

根据规则(3),线程A中写变量“x=42” Happens-Before 线程B中读变量“v==true”。


后面三条都是关于锁的。

(4)管程锁定规则

解锁Happens-Before于后续对这个锁的加锁。

(5)线程的start()规则

主线程start() 操作 Happens-Before 于子线程中的任意操作。

(6)线程的join()规则

主线程线程中,调用子线程的 join() 并成功返回,那么子线程中的任意操作 Happens-Before 于该 join() 操作的返回。

三个关键字

volatile可以禁用缓存以及编译优化,保证被它修饰的变量不存在可见性问题。但是频繁的访问volatile字段会因为不断地强制刷新缓存而影响性能。

synchronized是具备Happens-Before关系的,解锁操作Happens-Before于对同一把锁的加锁操作。实际上在解锁时,jvm会强制刷新缓存,使得当前线程修改的内容对其他线程可见。

final修饰的实例字段涉及到新建对象的发布问题,当一个对象包含被final修饰的实例字段时,其他线程可以看到已经初始化的final实例字段。

总结

在 Java 语言里面,Happens-Before 的语义本质上是一种可见性,A Happens-Before B 意味着 A 事件对 B 事件来说是可见的,无论 A 事件和 B 事件是否发生在同一个线程里。

对于java关键字 volatile、synchronized 和 final 的使用要注意,应该结合实际业务场景的需要来“按需禁用”计算机的优化,以达到性能最优。

相关文章

网友评论

      本文标题:java内存模型对并发问题的解决方案

      本文链接:https://www.haomeiwen.com/subject/nktprctx.html