首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Happen Before原则

Happen Before原则

原创
作者头像
兰亭集
发布2026-04-26 10:38:25
发布2026-04-26 10:38:25
220
举报
文章被收录于专栏:软件工程软件工程

内存模型

堆栈结构

内存模型

synchronized同步块

保证互斥性和可见性

  • 互斥性:一个时间只有一个线程可访问同步块逻辑
  • 可见性:
    • 进入锁时,从主存读取刷新变量值
    • 退出锁时,把变量的更新同步到主存中

volatile

volatile变量的读写直接发生在主存,volatile关键字也会防止volatile变量附近的指令重排,保证变量的可见性:

  • volatile变量写入完成,所有在volatile变量写入之前的写操作都会刷新到主存中,即volatile写触发一次写主存同步
  • volatile变量读取完成,所有在volatile变量读取之后的读操作都会从主存刷新,即volatile读触发一次读主存同步

volatile的可见性使用示例:FutureTask

FutureTask没有使用同步锁,而是使用volatile就保证了多线程访问下能拿到正确的结果。

代码语言:java
复制
public class FutureTask<V> implements RunnableFuture<V> {
    // state transitions: NEW -> COMPLETING -> NORMAL NEW -> COMPLETING -> EXCEPTIONAL NEW -> CANCELLED NEW -> INTERRUPTING -> INTERRUPTED
    private volatile int state;
    private static final int NEW          = 0;
    private static final int COMPLETING   = 1;
    private static final int NORMAL       = 2;
    private static final int EXCEPTIONAL  = 3;
    private static final int CANCELLED    = 4;
    private static final int INTERRUPTING = 5;
    private static final int INTERRUPTED  = 6;

    /** The underlying callable; nulled out after running */
    private Callable<V> callable;
    /** The result to return or exception to throw from get() */
    private Object outcome; // non-volatile, protected by state reads/writes
    /** The thread running the callable; CASed during run() */
    private volatile Thread runner;
    /** Treiber stack of waiting threads */
    private volatile WaitNode waiters;
}

这里只关注上面两个属性:

  • state:当前Future的状态,volatile变量
  • outcome:运行结果,outcome = callable.call(),非volatile变量,state变量的读写会保护outcome变量的读取

callable运行完成设置结果的逻辑是:

代码语言:java
复制
protected void set(V v) {
    if (STATE.compareAndSet(this, NEW, COMPLETING)) {
        outcome = v;
        STATE.setRelease(this, NORMAL); // state变量写入之前会把outcome变量先写入到主存
        finishCompletion();
    }
}

再看结果读取逻辑:

代码语言:java
复制
public V get() throws InterruptedException, ExecutionException {
    int s = state;
    if (s <= COMPLETING)
        s = awaitDone(false, 0L);
    return report(s);  // 读取state变量之后,会读取到outcome的主存最新值
}

private V report(int s) throws ExecutionException {
    Object x = outcome;
    if (s == NORMAL)
        return (V)x;
    if (s >= CANCELLED)
        throw new CancellationException();
    throw new ExecutionException((Throwable)x);
}

再比如AbstractQueuedSynchronizer也是用volatile保证同步块内变量的可见性。

双检锁 - double checked lock

单例模式懒加载的实现:

代码语言:java
复制
public class HeavyObject {
    private HeavyObject() {  // 构造函数私有
    }
    
    /**
     * 多线程场景下,指令重排可能会导致新建对象先赋值给引用,但对象内部变量尚未初始化完成,其他线程拿到了未初始化完成的对象
     * 加上volatile关键字防止指令重排
     * */
    private static volatile HeavyObject heavyObject;  
    
    public static HeavyObject getInstance() {  // 获取单例
        if (heavyObject == null) {
            synchronized (HeavyObject.class) {
                if (heavyObject == null) {
                    heavyObject = new HeavyObject();
                }
            }
        }
        return heavyObject;
    }
}

其他

  • volatile的long和double变量写入是原子的,非volatile的long和doube变量写入可能会被JVM拆成两次32bit的写入。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

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

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 内存模型
  • synchronized同步块
  • volatile
    • volatile的可见性使用示例:FutureTask
    • 双检锁 - double checked lock
    • 其他
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档