java 自旋方法_JAVA循环使用CAS实现自旋操作
大家碰到了實現(xiàn)一個線程安全的計數(shù)器的需求改怎么做呢?根據(jù)經(jīng)驗你應該知道我們要在多線程中實現(xiàn)共享變量的原子性和可見性問題,于是鎖成為一個不可避免的話題,下文討論的是與之對應的無鎖CAS。
為什么要無鎖
我們一想到在多線程下保證安全的方式,肯定是鎖,不管從硬件、操作系統(tǒng)層面都或多或少在使用鎖。鎖有優(yōu)缺點嗎?
使用鎖就需要獲得鎖、釋放鎖,CPU需要通過上下文切換和調(diào)度管理來進行這個操作,對于一個獨占鎖,一個線程在持有鎖后沒有執(zhí)行結(jié)束,其他線程就必須等待,等到前面的線程執(zhí)行完畢,CPU就會把鎖拿出來給其他線程來搶了。鎖的這種概念基于一種悲觀機制,它總是認為數(shù)據(jù)會被修改,所以,你在操作一部分代碼塊之前先加一把鎖,操作完成后再釋放,這樣就安全了。
什么是 CAS
比較并交換(compare and swap,CAS),是原子操作的一種,可用于在多線程編程中實現(xiàn)不被打斷的數(shù)據(jù)交換操作,從而避免多線程同時改寫某一數(shù)據(jù)時由于執(zhí)行順序不確定性以及中斷的不可預知性產(chǎn)生的數(shù)據(jù)不一致性問題。該操作通過將內(nèi)存中的值與指定的數(shù)據(jù)進行比較,當數(shù)值一樣時,將內(nèi)存中的數(shù)據(jù)替換成新值。
JAVA如何實現(xiàn)
package com.concurrent.program;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public class Counter {
private AtomicInteger atomicI = new AtomicInteger(0);
private int i = 0;
public static void main(String[] args) {
final Counter cas = new Counter();
Listts = new ArrayList(10);
long start = System.currentTimeMillis();
for (int j=0;j < 100; j++){
Thread t = new Thread(new Runnable() {
@Override
public void run() {
for (int i=0;i<10000; i++){
cas.count();
cas.safeCount();
}
}
});
ts.add(t);
}
for (Thread t : ts) {
t.start();
}
//等待所有線程執(zhí)行完成
for (Thread t:ts){
try {
t.join();
} catch (InterruptedException e){
e.printStackTrace();
}
}
System.out.println(cas.i);
System.out.println(cas.atomicI.get());
System.out.println(System.currentTimeMillis() - start);
}
/**
* 使用CAS實現(xiàn)線程安全計數(shù)器
*/
private void safeCount(){
for(;;){
int i = atomicI.get();
boolean suc = atomicI.compareAndSet(i, ++i);
if (suc) {
break;
}
}
}
/**
* 非線程安全計數(shù)器
*/
private void count(){
i++;
}
}
以上代碼實現(xiàn)了一個基于CAS線程安全的計數(shù)器方法safeCount和一個非線程安全的計數(shù)器方法count,讀者可以自行運行,運行結(jié)果如下:
非線程安全計數(shù)器: 971850
使用CAS實現(xiàn)線程安全計數(shù)器:?1000000
執(zhí)行時間:78ms
CAS存在的問題
1.ABA問題。因為CAS需要在操作值的時候,檢查值有沒有發(fā)生變化,如果沒有則更新。但是如果原來一個值是A,變成了B,又變成A,那么使用CAS進行檢查時會發(fā)現(xiàn)它的值沒有發(fā)生變化,但是實際發(fā)生了變化了。ABA解決問題的思路就是使用版本號。在變量前面追加版本號,每次變量更新的時候把版本號加1,那么A→B→A就會變成1A→2B→3A。從JAVA1.5開始,JDK的Atomic包里提供了一個類AtomicStampedReference來解決ABA問題。
2.循環(huán)時間長,開銷大。CAS長時間不成功,會給CPU帶來非常大的執(zhí)行開銷。如果JVM能支持處理器提供的pause指令,那么效率會有一定的提示。
3.只能保證一個共享變量的原子操作。對多個共享變量操作時,CAS可以把多個共享變量合并成一個共享變量來操作。比如,有兩個共享變量i=2,j=a,合并成ij=2a,然后用CAS來操作ij。從JAVA1.5開始,JDK的Atomic包里面提供了一個類AtomicReference類來保證引用對象之間的原子性,就可以把多個變量放在一個對象里面來進行CAS操作。
總結(jié)
以上是生活随笔為你收集整理的java 自旋方法_JAVA循环使用CAS实现自旋操作的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: go get如何删除_Go 每日一库之
- 下一篇: 韵乐x5效果器ktv最佳参数_家庭ktv