今天跟大家分享一下Java内存模型(JMM)之初识Happens-Before规则。
相信从事开发的同行们都知道,导致并发问题主要是可见性和有序性这两个问题。影响可见行性的原因是缓存,影响有序性的问题是编译器优化。如何去解决这两个问题呢,大家会说上锁,不管轻量级锁还是重量级锁,上就完了。volatile和synchronized和final三个关键字就会时常出现在大家的代码里(只管实现需求,不管服务器性能,who care?)。嘿嘿,放松一下心情,言归正传。下面我们就说一下什么是Happens-Before规则。
什么是Happens-Before?
在JMM中,在一个线程中,或不同的线程中。如果一个操作执行的结果需要对另一个操作可见,那么这两个操作之间必须存在Happens-Before关系。值得一提的是,我们说两个操作存在Happens-Before关系。这里指的操作的结果。并非执行顺序。
Happens-Before与我们密切相关的规则:
一个线程中的每个操作,Happens-Before于该线程中的任意后续操作;
例子:
public class HappensBefore {
static int a = 8;
static boolean flag = true;
public static void outPrint() {
if (flag) {
System.out.println(a);
}
}
public static void main(String args[]) {
HappensBefore.outPrint();
}
}
总结:这里我觉得可以通俗的总结下,就是程序是从上而下执行的。
对一个锁的解锁,Happens-Before于随后对这个锁的加锁;
例子:
public class HappensBefore {
long num = 0;
public synchronized void Increment() {
num++;
}
public synchronized void get() {
System.out.println(num);
}
}
总结:无论在单核CPU还是多核CPU的时候,指的是前一个线程的解锁操作对后一个线程的加锁操作可见。这个也叫做管程。
被volatile修改的变量写操作,Happens-Before于任意后续对这个变量操作的读;
例子:
public class HappensBefore {
static volatile long num = 0;
static Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
num ++;
}
});
static Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(num);
}
});
public static void main(String args[]) {
HappensBefore.thread1.start();
HappensBefore.thread2.start();
}
}
总结:当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量值刷新到主内存上。
当读一个volatile变量时,JMM会把该线程对应的本地缓存置为无效,从主内存中重新读取变量。
A Happens-Before B ,B Happens-Before C,那么 A Happens-Before C
如下图:
例子:
public class HappensBefore {
static int x = 0;
static volatile boolean flag = false;
static Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
x = 42; //1⃣️
flag = true;// 2⃣️
}
});
static Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
if (flag) {3⃣️
System.out.println(x);
}
}
});
}
总结:1⃣️ Happens-Before 2⃣️ , 2⃣️ Happens-Before 3⃣️ , 1⃣️ Happens-Before 3⃣️ 。
所以当线程2读取到flag为真的时候,就自然而然的知道了,x的值是42了。
给大家出一个问题,希望大家积极回答!
public class Singleton {
private static Singleton singleton;
public Singleton() {}
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
这段代码少了什么?应该加上什么就完美了?然后说明一下为什么要加这个?