Java原子量

所謂的原子量即操做變量的操做是「原子的」,該操做不可再分,所以是線程安全的。java

 爲什麼要使用原子變量呢,緣由是多個線程對單個變量操做也會引發一些問題。在Java5以前,能夠經過volatile、synchronized關鍵字來解決併發訪問的安全問題,但這樣太麻煩。安全

Java5以後,專門提供了用來進行單變量多線程併發安全訪問的工具包java.util.concurrent.atomic,其中的類也很簡單。多線程

下面給出一個反面例子(切勿模仿):併發

import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 
import java.util.concurrent.atomic.AtomicLong; 
/** 
* Java線程:新特徵-原子量 
* 
* @author leizhimin 2009-11-6 9:53:11 
*/
 
public
 
 class Test { 
        public static void main(String[] args) { 
                ExecutorService pool = Executors.newFixedThreadPool(2); 
                Runnable t1 = new MyRunnable("張三", 2000); 
                Runnable t2 = new MyRunnable("李四", 3600); 
                Runnable t3 = new MyRunnable("王五", 2700); 
                Runnable t4 = new MyRunnable("老張", 600); 
                Runnable t5 = new MyRunnable("老牛", 1300); 
                Runnable t6 = new MyRunnable("胖子", 800); 
                //執行各個線程 
                pool.execute(t1); 
                pool.execute(t2); 
                pool.execute(t3); 
                pool.execute(t4); 
                pool.execute(t5); 
                pool.execute(t6); 
                
                //關閉線程池 
                pool.shutdown(); 
        } 
} 
class MyRunnable implements Runnable { 
        private static AtomicLong aLong = new AtomicLong(10000);      //原子量,每一個線程均可以自由操做 
        private String name;                //操做人 
        private int x;                            //操做數額 
        MyRunnable(String name, int x) { 
                this.name = name;                 
                this.x = x; 
        } 
        public void run() { 
                        System.out.println(name + "執行了" + x + ",當前餘額:" + aLong.addAndGet(x)); 
        } 
}

運行結果:工具

李四執行了3600,當前餘額:13600 
王五執行了2700,當前餘額:16300 
老張執行了600,當前餘額:16900 
老牛執行了1300,當前餘額:18200 
胖子執行了800,當前餘額:19000 
張三執行了2000,當前餘額:21000 

Process finished with exit code 0this

 

張三執行了2000,當前餘額:12000 
王五執行了2700,當前餘額:18300 
老張執行了600,當前餘額:18900 
老牛執行了1300,當前餘額:20200 
胖子執行了800,當前餘額:21000 
李四執行了3600,當前餘額:15600 

Process finished with exit code 0atom

 

張三執行了2000,當前餘額:12000 
李四執行了3600,當前餘額:15600 
老張執行了600,當前餘額:18900 
老牛執行了1300,當前餘額:20200 
胖子執行了800,當前餘額:21000 
王五執行了2700,當前餘額:18300 

Process finished with exit code 0線程

 

從運行結果能夠看出,雖然使用了原子量,可是程序併發訪問仍是有問題,那究竟問題出在哪裏了?code

這裏要注意的一點是,原子量雖然能夠保證單個變量在某一個操做過程的安全,但沒法保證你整個代碼塊,或者整個程序的安全性。所以,一般還應該使用鎖等同步機制來控制整個程序的安全性。
對象


下面是對這個錯誤修正:

import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 
import java.util.concurrent.locks.Lock; 
import java.util.concurrent.locks.ReentrantLock; 
import java.util.concurrent.atomic.AtomicLong; 
/** 
* Java線程:新特徵-原子量 
* 
* @author leizhimin 2009-11-6 9:53:11 
*/
 
public class Test { 
        public static void main(String[] args) { 
                ExecutorService pool = Executors.newFixedThreadPool(2); 
                Lock lock = new ReentrantLock(false); 
                Runnable t1 = new MyRunnable("張三", 2000,lock); 
                Runnable t2 = new MyRunnable("李四", 3600,lock); 
                Runnable t3 = new MyRunnable("王五", 2700,lock); 
                Runnable t4 = new MyRunnable("老張", 600,lock); 
                Runnable t5 = new MyRunnable("老牛", 1300,lock); 
                Runnable t6 = new MyRunnable("胖子", 800,lock); 
                
                //執行各個線程 
                pool.execute(t1); 
                pool.execute(t2); 
                pool.execute(t3); 
                pool.execute(t4); 
                pool.execute(t5); 
                pool.execute(t6); 
                
                //關閉線程池 
                pool.shutdown(); 
        } 
} 
    class MyRunnable implements Runnable { 
        private static AtomicLong aLong = new AtomicLong(10000);     //原子量,每一個線程均可以由操做 
        private String name;                //操做人 
        private int x;                     //操做數額 
        private Lock lock; 
        MyRunnable(String name, int x,Lock lock) { 
                this.name = name; 
                this.x = x; 
                this.lock = lock; 
        } 
        public void run() { 
                lock.lock(); 
                System.out.println(name + "執行了" + x + ",當前餘額:" + aLong.addAndGet(x)); 
                lock.unlock(); 
        } 
}

 

執行結果:

張三執行了2000,當前餘額:12000 
王五執行了2700,當前餘額:14700 
老張執行了600,當前餘額:15300 
老牛執行了1300,當前餘額:16600 
胖子執行了800,當前餘額:17400 
李四執行了3600,當前餘額:21000 
Process finished with exit code 0

這裏使用了一個對象鎖,來控制對併發代碼的訪問。無論運行多少次,執行次序如何,最終餘額均爲21000,這個結果是正確的。 有關原子量的用法很簡單,關鍵是對原子量的認識。原子量操做的是原子性的,但整個程序不線程安全的

相關文章
相關標籤/搜索