前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JMM之Happens-Before规则

JMM之Happens-Before规则

作者头像
Spark学习技巧
发布2019-06-14 09:51:18
6130
发布2019-06-14 09:51:18
举报
文章被收录于专栏:Spark学习技巧Spark学习技巧

今天跟大家分享一下Java内存模型(JMM)之初识Happens-Before规则。

相信从事开发的同行们都知道,导致并发问题主要是可见性和有序性这两个问题。影响可见行性的原因是缓存,影响有序性的问题是编译器优化。如何去解决这两个问题呢,大家会说上锁,不管轻量级锁还是重量级锁,上就完了。volatile和synchronized和final三个关键字就会时常出现在大家的代码里(只管实现需求,不管服务器性能,who care?)。嘿嘿,放松一下心情,言归正传。下面我们就说一下什么是Happens-Before规则。

什么是Happens-Before?

在JMM中,在一个线程中,或不同的线程中。如果一个操作执行的结果需要对另一个操作可见,那么这两个操作之间必须存在Happens-Before关系。值得一提的是,我们说两个操作存在Happens-Before关系。这里指的操作的结果。并非执行顺序。

Happens-Before与我们密切相关的规则:

1.程序顺序规则

一个线程中的每个操作,Happens-Before于该线程中的任意后续操作;

例子:

代码语言:javascript
复制
 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();
            }
        }

总结:这里我觉得可以通俗的总结下,就是程序是从上而下执行的。

2.监视器锁规则

对一个锁的解锁,Happens-Before于随后对这个锁的加锁;

例子:

代码语言:javascript
复制
public class HappensBefore {
 
            long num = 0;
 
            public synchronized void Increment() {
                num++;
            }
 
            public synchronized void get() {
                System.out.println(num);
            }
        }

总结:无论在单核CPU还是多核CPU的时候,指的是前一个线程的解锁操作对后一个线程的加锁操作可见。这个也叫做管程。

3.volatile变量规则

被volatile修改的变量写操作,Happens-Before于任意后续对这个变量操作的读;

例子:

代码语言:javascript
复制
 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会把该线程对应的本地缓存置为无效,从主内存中重新读取变量。

4.传递性

A Happens-Before B ,B Happens-Before C,那么 A Happens-Before C

如下图:

例子:

代码语言:javascript
复制
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了。

给大家出一个问题,希望大家积极回答!

代码语言:javascript
复制
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;
    }
}

这段代码少了什么?应该加上什么就完美了?然后说明一下为什么要加这个?

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-06-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 浪尖聊大数据 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.程序顺序规则
  • 2.监视器锁规则
  • 3.volatile变量规则
  • 4.传递性
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档