Java并发-02-Java内存模型

说到java并发,不得不提出java内存模型(JMM);Java内存模型规范了JVM如何提供按需禁用缓存和编译优化的方法。具体来说,这些方法包括 volatilesynchronizedfinal 三个关键字,以及Happens-Before 规则。

Java内存模型底层实现

主要是通过内存屏障(memory barrier)禁止重排序的,即时编译器根据具体的底层体系架构,将这些内存屏障替换成具体的 CPU 指令。对于编译器而言,内存屏障将限制它所能做的重排序优化。而对于处理器而言,内存屏障将会导致缓存的刷新操作。比如,对于volatile,编译器将在volatile字段的读写操作前后各插入一些内存屏障。

JSR133 中的FAQ

http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html

以下英文翻译如果不对,欢迎批评指正

the goal of JSR 133 was to create a set of formal semantics that provides an intuitive framework for how volatile, synchronized, and final work.

翻译一下:JSR133的目的是创建一组正式语义的直观架构,包含volatile、synchronzied和final如何工作的。

The goals of JSR 133 include:

  • Preserving existing safety guarantees, like type-safety, and strengthening others. For example, variable values may not be created "out of thin air": each value for a variable observed by some thread must be a value that can reasonably be placed there by some thread.

  • The semantics of correctly synchronized programs should be as simple and intuitive as possible.

  • The semantics of incompletely or incorrectly synchronized programs should be defined so that potential security hazards are minimized.

  • Programmers should be able to reason confidently about how multithreaded programs interact with memory.

  • It should be possible to design correct, high performance JVM implementations across a wide range of popular hardware architectures.

  • A new guarantee of initialization safety should be provided. If an object is properly constructed (which means that references to it do not escape during construction), then all threads which see a reference to that object will also see the values for its final fields that were set in the constructor, without the need for synchronization.

  • There should be minimal impact on existing code.

1-Happens-Before 规则

Happens-Before的语义本质上是一种可见性,A Happens-Before B 意味着A事件对B事件来说是可见的,无论A事件和B事件是否发生在同一个线程里。例如A事件发生在线程1上,B事件发生在线程2上,Happens-Before规则保证线程2上也能看到A事件的发生

传递性:

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

(1)程序的顺序性规则:

在一个线程中,按照程序顺序,前面的操作 Happens-Before 于后续的任意操作。

(2)volatile变量规则:

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

(3)锁的规则:

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

ps:管程是一种通用的同步原语,在Java中指的就是synchronized,synchronized是Java里对管程的实现。

(4)线程 start() 规则:

主线程A启动子线程B后,子线程B能够看到主线程在启动子线程B前的操作。

Thread B = new Thread(()->{
  // 主线程调用B.start()之前
  // 所有对共享变量的修改,此处皆可见
  // 此例中,var==77
});
// 此处对共享变量var修改
var = 77;
// 主线程启动子线程
B.start();

(5)线程 join() 规则:

主线程A等待子线程B完成(主线程A通过调用子线程B的join()方法实现),当子线程B完成后(主线程A中join()方法返回),主线程能够看到子线程的操作。当然所谓的“看到”,指的是对共享变量的操作。

Thread B = new Thread(()->{
  // 此处对共享变量var修改
  var = 66;
});
// 例如此处对共享变量修改,
// 则这个修改结果对线程B可见
// 主线程启动子线程
B.start();
B.join()
// 子线程所有对共享变量的修改
// 在主线程调用B.join()之后皆可见
// 此例中,var==66

(6)线程中断规则:

对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生,可以通过Thread.interrupted()方法检测到是否有中断发生。

(7)对象终结规则:

一个对象的初始化完成(构造函数执行结束)先行发生于它的finalize()方法的开始。

2-volatile

Volatile fields are special fields which are used for communicating state between threads. Each read of a volatile will see the last write to that volatile by any thread; in effect, they are designated by the programmer as fields for which it is never acceptable to see a "stale" value as a result of caching or reordering. The compiler and runtime are prohibited from allocating them in registers. They must also ensure that after they are written, they are flushed out of the cache to main memory, so they can immediately become visible to other threads. Similarly, before a volatile field is read, the cache must be invalidated so that the value in main memory, not the local processor cache, is the one seen. There are also additional restrictions on reordering accesses to volatile variables.

volatile:保证可见性;

如果一个变量定义为volatile变量,对这个变量的读写,不能使用CPU缓存,必须从内存中读取或者写入...

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

class VolatileExample {
  int x = 0;
  volatile boolean v = false;
  public void writer() {
    x = 42;
    v = true;
  }

  public void reader() {
    if (v == true) {
      //uses x - guaranteed to see 42.
    }
  }
}

3-final

How do final fields work under the new JMM?

The values for an object's final fields are set in its constructor. Assuming the object is constructed "correctly", once an object is constructed, the values assigned to the final fields in the constructor will be visible to all other threads without synchronization. In addition, the visible values for any other object or array referenced by those final fields will be at least as up-to-date as the final fields.

What does it mean for an object to be properly constructed? It simply means that no reference to the object being constructed is allowed to "escape" during construction. (See Safe Construction Techniques for examples.) In other words, do not place a reference to the object being constructed anywhere where another thread might be able to see it; do not assign it to a static field, do not register it as a listener with any other object, and so on. These tasks should be done after the constructor completes, not in the constructor.

class FinalFieldExample {
  final int x;
  int y;
  static FinalFieldExample f;
  public FinalFieldExample() {
    x = 3;
    y = 4;
  }

  static void writer() {
    f = new FinalFieldExample();
  }

  static void reader() {
    if (f != null) {
      int i = f.x;
      int j = f.y;
    }
  }
}
//The class above is an example of how final fields should be used. A thread executing reader is guaranteed to see the value 3 for f.x, because it is final. It is not guaranteed to see the value 4 for y, because it is not final
//A线程调用writer方法,然后B调用reader,B读到的i一定是3(final修饰),j可能是0或者4

在构造函数里面将this赋值给了全局变量global.obj,这就是“逸出”,线程通过global.obj读取x是有可能读到0的。因此我们一定要避免“逸出”。

public FinalFieldExample() { // bad!
  x = 3;
  y = 4;
  // bad construction - allowing this to escape  // 此处就是讲this逸出,
  global.obj = this;
}
//then threads that read the reference to this from global.obj are not guaranteed to see 3 for x.

4-synchronized

原子性:一个或者多个操作在CPU执行的过程中不被中断的特性。

synchronized是比volatile更加重量级的,虚拟机保证的,它同时保证原子性和可见性;而且在后续的jdk版本中,对synchronized进行各种优化,偏向锁,轻量级锁等等,性能得到很大的提升。

锁是一种通用的技术方案,Java语言提供的synchronized关键字,就是锁的一种实现。synchronized关键字可以用来修饰方法,也可以用来修饰代码块。

  • 当修饰静态方法的时候,锁定的是当前类的Class对象,在上面的例子中就是Class X;

  • 当修饰非静态方法的时候,锁定的是当前实例对象this。

关于synchronized锁本质是monitor对象,对象头相关后续补充;所以每个Java对象都是可以作为一把锁和synchronized配合使用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值