原子量

所谓的原子量即操作变量的操作是“原子的”,该操作不可再分,因此是线程安全的。
 
为何要使用原子变量呢,原因是多个线程对单个变量操作也会引起一些问题。在Java5之前,可以通过volatile、synchronized关键字来解决并发访问的安全问题,但这样太麻烦。
Java5之后,专门提供了用来进行单变量多线程并发安全访问的工具包java.util.concurrent.atomic,其中的类也很简单。
 
下面给出一个反面例子(切勿模仿):
Java代码 复制代码
  1. import java.util.concurrent.ExecutorService;    
  2. import java.util.concurrent.Executors;    
  3. import java.util.concurrent.atomic.AtomicLong;    
  4.   
  5. /**   
  6. * Java线程:新特征-原子量   
  7.  
  8. * @author leizhimin 2009-11-6 9:53:11   
  9. */    
  10. public class Test {    
  11.         public static void main(String[] args) {    
  12.                 ExecutorService pool = Executors.newFixedThreadPool(2);    
  13.                 Runnable t1 = new MyRunnable("张三"2000);    
  14.                 Runnable t2 = new MyRunnable("李四"3600);    
  15.                 Runnable t3 = new MyRunnable("王五"2700);    
  16.                 Runnable t4 = new MyRunnable("老张"600);    
  17.                 Runnable t5 = new MyRunnable("老牛"1300);    
  18.                 Runnable t6 = new MyRunnable("胖子"800);    
  19.                 //执行各个线程    
  20.                 pool.execute(t1);    
  21.                 pool.execute(t2);    
  22.                 pool.execute(t3);    
  23.                 pool.execute(t4);    
  24.                 pool.execute(t5);    
  25.                 pool.execute(t6);    
  26.                 //关闭线程池    
  27.                 pool.shutdown();    
  28.         }    
  29. }    
  30.   
  31. class MyRunnable implements Runnable {    
  32.         private static AtomicLong aLong = new AtomicLong(10000);        //原子量,每个线程都可以自由操作    
  33.         private String name;                //操作人    
  34.         private int x;                            //操作数额    
  35.   
  36.         MyRunnable(String name, int x) {    
  37.                 this.name = name;    
  38.                 this.x = x;    
  39.         }    
  40.   
  41.         public void run() {    
  42.                 System.out.println(name + "执行了" + x + ",当前余额:" + aLong.addAndGet(x));    
  43.         }    
  44. }  
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)); 
        } 
}
 运行结果:
Java代码 复制代码
  1. 李四执行了3600,当前余额:13600    
  2. 王五执行了2700,当前余额:16300    
  3. 老张执行了600,当前余额:16900    
  4. 老牛执行了1300,当前余额:18200    
  5. 胖子执行了800,当前余额:19000    
  6. 张三执行了2000,当前余额:21000    
  7.   
  8. Process finished with exit code 0  
李四执行了3600,当前余额:13600 
王五执行了2700,当前余额:16300 
老张执行了600,当前余额:16900 
老牛执行了1300,当前余额:18200 
胖子执行了800,当前余额:19000 
张三执行了2000,当前余额:21000 

Process finished with exit code 0
 
Java代码 复制代码
  1. 张三执行了2000,当前余额:12000    
  2. 王五执行了2700,当前余额:18300    
  3. 老张执行了600,当前余额:18900    
  4. 老牛执行了1300,当前余额:20200    
  5. 胖子执行了800,当前余额:21000    
  6. 李四执行了3600,当前余额:15600    
  7.   
  8. Process finished with exit code 0  
张三执行了2000,当前余额:12000 
王五执行了2700,当前余额:18300 
老张执行了600,当前余额:18900 
老牛执行了1300,当前余额:20200 
胖子执行了800,当前余额:21000 
李四执行了3600,当前余额:15600 

Process finished with exit code 0
 
Java代码 复制代码
  1. 张三执行了2000,当前余额:12000    
  2. 李四执行了3600,当前余额:15600    
  3. 老张执行了600,当前余额:18900    
  4. 老牛执行了1300,当前余额:20200    
  5. 胖子执行了800,当前余额:21000    
  6. 王五执行了2700,当前余额:18300    
  7.   
  8. Process finished with exit code 0  
张三执行了2000,当前余额:12000 
李四执行了3600,当前余额:15600 
老张执行了600,当前余额:18900 
老牛执行了1300,当前余额:20200 
胖子执行了800,当前余额:21000 
王五执行了2700,当前余额:18300 

Process finished with exit code 0
 
从运行结果可以看出,虽然使用了原子量,但是程序并发访问还是有问题,那究竟问题出在哪里了?
 
这里要注意的一点是,原子量虽然可以保证单个变量在某一个操作过程的安全,但无法保证你整个代码块,或者整个程序的安全性。因此,通常还应该使用锁等同步机制来控制整个程序的安全性。
 
下面是对这个错误修正:
Java代码 复制代码
  1. import java.util.concurrent.ExecutorService;    
  2. import java.util.concurrent.Executors;    
  3. import java.util.concurrent.locks.Lock;    
  4. import java.util.concurrent.locks.ReentrantLock;    
  5. import java.util.concurrent.atomic.AtomicLong;    
  6.   
  7. /**   
  8. * Java线程:新特征-原子量   
  9.  
  10. * @author leizhimin 2009-11-6 9:53:11   
  11. */    
  12. public class Test {    
  13.         public static void main(String[] args) {    
  14.                 ExecutorService pool = Executors.newFixedThreadPool(2);    
  15.                 Lock lock = new ReentrantLock(false);    
  16.                 Runnable t1 = new MyRunnable("张三"2000,lock);    
  17.                 Runnable t2 = new MyRunnable("李四"3600,lock);    
  18.                 Runnable t3 = new MyRunnable("王五"2700,lock);    
  19.                 Runnable t4 = new MyRunnable("老张"600,lock);    
  20.                 Runnable t5 = new MyRunnable("老牛"1300,lock);    
  21.                 Runnable t6 = new MyRunnable("胖子"800,lock);    
  22.                 //执行各个线程    
  23.                 pool.execute(t1);    
  24.                 pool.execute(t2);    
  25.                 pool.execute(t3);    
  26.                 pool.execute(t4);    
  27.                 pool.execute(t5);    
  28.                 pool.execute(t6);    
  29.                 //关闭线程池    
  30.                 pool.shutdown();    
  31.         }    
  32. }    
  33.   
  34. class MyRunnable implements Runnable {    
  35.         private static AtomicLong aLong = new AtomicLong(10000);        //原子量,每个线程都可以自由操作    
  36.         private String name;                //操作人    
  37.         private int x;                            //操作数额    
  38.         private Lock lock;    
  39.   
  40.         MyRunnable(String name, int x,Lock lock) {    
  41.                 this.name = name;    
  42.                 this.x = x;    
  43.                 this.lock = lock;    
  44.         }    
  45.   
  46.         public void run() {    
  47.                 lock.lock();    
  48.                 System.out.println(name + "执行了" + x + ",当前余额:" + aLong.addAndGet(x));    
  49.                 lock.unlock();    
  50.         }    
  51. }  
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(); 
        } 
}
 
执行结果:
Java代码 复制代码
  1. 张三执行了2000,当前余额:12000    
  2. 王五执行了2700,当前余额:14700    
  3. 老张执行了600,当前余额:15300    
  4. 老牛执行了1300,当前余额:16600    
  5. 胖子执行了800,当前余额:17400    
  6. 李四执行了3600,当前余额:21000    
  7.   
  8. Process finished with exit code 0  
张三执行了2000,当前余额:12000 
王五执行了2700,当前余额:14700 
老张执行了600,当前余额:15300 
老牛执行了1300,当前余额:16600 
胖子执行了800,当前余额:17400 
李四执行了3600,当前余额:21000 

Process finished with exit code 0
 
这里使用了一个对象锁,来控制对并发代码的访问。不管运行多少次,执行次序如何,最终余额均为21000,这个结果是正确的。
 
有关原子量的用法很简单,关键是对原子量的认识,原子仅仅是保证变量操作的原子性,但整个程序还需要考虑线程安全的。

你可能感兴趣的:(java,多线程,Blog)