Java線程-線程池-原子操作

原子操作是指一個不受其他操作影響的操作任務單元。原子操作是在多線程環境下避免數據不一致必須的手段。
int++並不是一個原子操作,所以當一個線程讀取它的值並加1時,另外一個線程有可能會讀到之前的值,這就會引發錯誤。
爲了解決這個問題,必須保證增加操作是原子的,可以通過volatile、synchronized關鍵字來解決併發訪問的安全問題,但是這樣解決太麻煩。
java.util.concurrent.atomic包提供了int和long類型的裝類,它們可以自動的保證對於他們的操作是原子的並且不需要使用同步。

下面給出一個反面例子:

public class Main {
    public static void main(String args[]){
        //創建一個線程池
        ExecutorService executorService= Executors.newFixedThreadPool(2);
        //創建五個線程
        Runnable runnable1=new RunnableImpl("張三",2000);
        Runnable runnable2=new RunnableImpl("李四",1000);
        Runnable runnable3=new RunnableImpl("王五",3000);
        Runnable runnable4=new RunnableImpl("趙六",4000);
        Runnable runnable5=new RunnableImpl("陳七",2000);
        Runnable runnable6=new RunnableImpl("王八",2000);
        Runnable runnable7=new RunnableImpl("毛九",5000);
        //執行各個線程
        executorService.execute(runnable1);
        executorService.execute(runnable2);
        executorService.execute(runnable3);
        executorService.execute(runnable4);
        executorService.execute(runnable5);
        executorService.execute(runnable6);
        executorService.execute(runnable7);
        //關閉線程池
        executorService.shutdown();
    }

}
class RunnableImpl implements Runnable {
    private static AtomicLong atomicLong=new AtomicLong(10000);
    private String name;    //操作人
    private int x;          //操作金額
    RunnableImpl(String name,int x){
        this.name=name;
        this.x=x;
    }
    @Override
    public void run() {
        System.out.println(name+"執行了"+x+",當前餘額:"+atomicLong.addAndGet(x));
    }
}

運行結果如下:

張三執行了2000,當前餘額:12000
王五執行了3000,當前餘額:16000
趙六執行了4000,當前餘額:20000
陳七執行了2000,當前餘額:22000
王八執行了2000,當前餘額:24000
毛九執行了5000,當前餘額:29000
李四執行了1000,當前餘額:13000
張三執行了2000,當前餘額:12000
李四執行了1000,當前餘額:13000
王五執行了3000,當前餘額:16000
趙六執行了4000,當前餘額:20000
陳七執行了2000,當前餘額:22000
王八執行了2000,當前餘額:24000
毛九執行了5000,當前餘額:29000

從結果可以看出,這樣的計算結果還是存在問題,原子量雖然保證了單個變量在某一個操作上的安全,但是它無法保證整個程序的安全性,下面對錯誤進行修正:

public class Main {
    public static void main(String args[]){
        //創建一個線程池
        ExecutorService executorService= Executors.newFixedThreadPool(2);
        //創建一個鎖對象
        Lock lock=new ReentrantLock(false);
        //創建五個線程
        Runnable runnable1=new RunnableImpl("張三",2000,lock);
        Runnable runnable2=new RunnableImpl("李四",1000,lock);
        Runnable runnable3=new RunnableImpl("王五",3000,lock);
        Runnable runnable4=new RunnableImpl("趙六",4000,lock);
        Runnable runnable5=new RunnableImpl("陳七",2000,lock);
        Runnable runnable6=new RunnableImpl("王八",2000,lock);
        Runnable runnable7=new RunnableImpl("毛九",5000,lock);
        //執行各個線程
        executorService.execute(runnable1);
        executorService.execute(runnable2);
        executorService.execute(runnable3);
        executorService.execute(runnable4);
        executorService.execute(runnable5);
        executorService.execute(runnable6);
        executorService.execute(runnable7);
        //關閉線程池
        executorService.shutdown();
    }

}
class RunnableImpl implements Runnable {
    private static AtomicLong atomicLong=new AtomicLong(10000);
    private String name;    //操作人
    private int x;          //操作金額
    private Lock lock;
    RunnableImpl(String name,int x,Lock lock){
        this.name=name;
        this.x=x;
        this.lock=lock;
    }
    @Override
    public void run() {
        lock.lock();
        System.out.println(name+"執行了"+x+",當前餘額:"+atomicLong.addAndGet(x));
        lock.unlock();
    }
}

運行結果如下:

張三執行了2000,當前餘額:12000
王五執行了3000,當前餘額:15000
趙六執行了4000,當前餘額:19000
陳七執行了2000,當前餘額:21000
王八執行了2000,當前餘額:23000
毛九執行了5000,當前餘額:28000
李四執行了1000,當前餘額:29000
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章