本文作者 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 转载请注明
评论