偽共享問題的表現(xiàn)是:并發(fā)的修改在一個緩存行中的多個獨立變量,看起來是并發(fā)執(zhí)行的恃泪,但實際在CPU處理的時候湿硝,是串行執(zhí)行的,并發(fā)的性能大打折扣僵控。
這個涉及到 MESI(緩存一致性協(xié)議)香到,參考鏈接:偽共享問題
偽共享的原因就是 CPU 在 Invalid
的時候,是會直接廢除一行的报破!
如果 兩個變量 (a,b)
同時在一個 Cache Line
中悠就,處理器A
修改了變量a
,那么處理器B中充易,這個 CacheLine
失效了梗脾,這個時候如果處理器B
修改了變量b
的話,就必須先提交處理器A
的緩存盹靴,然后處理器B
再去主存中讀取數(shù)據(jù)炸茧!這樣就出現(xiàn)了問題,a
和b
在兩個處理器上被修改稿静,本應(yīng)該是一個并行的操作梭冠,但是由于緩存一致性,卻成為了串行改备!這樣會嚴重的影響并發(fā)的性能控漠!
解決方案:
Java中提供給了我們兩種方案:
填充法
和 Contended
注解
- 填充法:就是
擴大對象的大小
,這樣悬钳,就可以一個緩沖行中盐捷,只存在一個對象!這樣默勾,就不會導致結(jié)果是串行執(zhí)行了碉渡!
public class DataPadding{
long a1,a2,a3,a4,a5,a6,a7,a8;//防止與前一個對象產(chǎn)生偽共享
int value;
long modifyTime;
long b1,b2,b3,b4,b5,b6,b7,b8;//防止不相關(guān)變量偽共享;
boolean flag;
long c1,c2,c3,c4,c5,c6,c7,c8;//
long createTime;
char key;
long d1,d2,d3,d4,d5,d6,d7,d8;//防止與下一個對象產(chǎn)生偽共享
}
上面的代碼使用,填充法母剥,對象的屬性在內(nèi)存行中的布局如下
value , modifyTime
flag
createTime
key
- Contended 注解法:Java1.8 中提供了
Contended
注解滞诺,使用這個注解形导,VM必須設(shè)置-XX:-RestrictContended
如果在類型上添加Contended注解,那么這個類的對象的每個屬性都會在不同的CacheLine
中
如果在屬性上設(shè)置Contended
铭段,那么可以指定哪些 屬性 處于一個CacheLine
中
@SuppressWarnings("restriction")
public class ContendedGroupData {
@sun.misc.Contended("group1")
int value;
@sun.misc.Contended("group1")
long modifyTime;
@sun.misc.Contended("group2")
boolean flag;
@sun.misc.Contended("group3")
long createTime;
@sun.misc.Contended("group3")
char key;
}
這個偽共享的注解骤宣,在 LongAddr
和 ConcurrentHashMap
中得到了很多的運用,對于并發(fā)的修改一個對象中的多個屬性的時候序愚,應(yīng)該防止偽共享!