volatile 关键字需要知道的几点

本文作者 https://github.com/lich0079 转载请注明

可见性

多核执行多线程的情况下,每个core读取变量不是直接从内存读,而是从L1, L2 …cache读,所以你在一个core中的write不一定会被其他core马上观测到。

解决这个的办法就是volatile关键字,加上它修饰后,变量在一个core中做了修改,会导致其他core的缓存立即失效,这样就会从内存中读出最新的值,保证了可见性。

False sharing

volatile 导致其他core缓存失效会带来一个问题。假设2个volatile变量位于一个缓存单元中(cache line,通常64字节),那么一个core一直修改变量A,另一个core在使用变量B,A的不断更新导致在另一个core中处于同一个cache line中的B也会需要一直去内存拉最新的值,即使另一个完全不关心A的值。

解决这个问题的方法就是Padding,把volatile关键字的周围用其他的值来填充,保证volatile不会和其他的volatile共享一个cache line

class LhsPadding
{
    protected long p1, p2, p3, p4, p5, p6, p7;
}

class Value extends LhsPadding
{
    protected volatile long value;
}

class RhsPadding extends Value
{
    protected long p9, p10, p11, p12, p13, p14, p15;
}

public class Sequence extends RhsPadding
{
    public long get()
    {
        return value;
    }
}

lazyset

加了volatile的变量可以保证对它的修改的可见性,但这种保证也是有性能的代价的。那么对一个volatile变量我可不可以有时候不需要它的可见性保证来提升性能呢?

答案就是sun.misc.Unsafe

does not guarantee immediate visibility of the store to other threads. This method is generally only useful if the underlying field is a Java volatile (or if an array cell, one that is otherwise only accessed using volatile accesses).

public native void    putOrderedLong(Object o, long offset, long x);

用法如下, 注意里面对 value的set 是有 volatile 和 non volatile 2个版本,分别针对性能和可见性的场合。

package com.lmax.disrupto;

public class Sequence extends RhsPadding
{
    static final long INITIAL_VALUE = -1L;
    private static final Unsafe UNSAFE;
    private static final long VALUE_OFFSET;

    static
    {
        UNSAFE = Util.getUnsafe();
        try
        {
            VALUE_OFFSET = UNSAFE.objectFieldOffset(Value.class.getDeclaredField("value"));
        }
        catch (final Exception e)
        {
            throw new RuntimeException(e);
        }
    }
    /**
     * Perform an ordered write of this sequence.  The intent is
     * a Store/Store barrier between this write and any previous
     * store.
     *
     * @param value The new value for the sequence.
     */
    public void set(final long value)
    {
        UNSAFE.putOrderedLong(this, VALUE_OFFSET, value);
    }

    /**
     * Performs a volatile write of this sequence.  The intent is
     * a Store/Store barrier between this write and any previous
     * write and a Store/Load barrier between this write and any
     * subsequent volatile read.
     *
     * @param value The new value for the sequence.
     */
    public void setVolatile(final long value)
    {
        UNSAFE.putLongVolatile(this, VALUE_OFFSET, value);
    }
}

本文作者 https://github.com/lich0079 转载请注明

原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/tech/pnotes/175729.html

(0)
上一篇 2021年10月14日 02:31
下一篇 2021年10月14日 02:56

相关推荐

发表回复

登录后才能评论