Java:volatile

Java中的原子性操作与同步问题

1.在Java中,原子操作是指不能被线程调度机制中断的操作,一旦操作开始,那么它一定可以在可能发生的"上下文切换"之前(切换到其它线程执行)执行完毕。

2.依赖原子性来处理同步问题时很棘手并且很危险的事情。

如下面的程序,尽管return i;确实是原子性操作,但是缺少同步使得其数值可以在处于不稳定的中间状态时被读取,尽管变量i添加上了volatile关键字。

正确的方法时同时将getValue()和evenIncrement()都是synchronized的。

 1 import java.util.concurrent.ExecutorService;
 2 import java.util.concurrent.Executors;
 3 
 4 
 5 public class AtomicityTest implements Runnable {
 6 
 7     private int i = 0;
 8     
 9     public int getValue() {
10         return i;
11     }
12     
13     private synchronized void evenIncrement() {
14         i++;
15         i++;
16     }
17     
18     @Override
19     public void run() {
20         while (true) {
21             evenIncrement();
22         }
23     }
24     
25     public static void main(String[] args) {
26         ExecutorService exec = Executors.newCachedThreadPool();
27         AtomicityTest at = new AtomicityTest();
28         exec.execute(at);
29         while (true) {
30             int val = at.getValue();
31             if (val % 2 != 0) {
32                 System.out.println("error..." + val + " is odd.");
33                 System.exit(0);
34             }
35         }
36  
37     }
38 
39 }
40 /**
41  * 程序运行结果:
42  * error...53683 is odd.
43  */
AtomicityTest.java

再如下面的程序,一方面虽然用volatile关键字修饰了SerialNumberGenerator.serialNumber成员,但是nextSerialNumber()中的++运算并不是原子操作,所以还是会出现问题。

解决问题得方法就是把nextSerialNumber()方法改为synchronized的。

1 public class SerialNumberGenerator {
2     
3     private static volatile int serialNumber = 0;
4     
5     public static int nextSerialNumber() {
6         return serialNumber++;
7     }
8 }
SerialNumberGenerator.java

 1 public class CircularSet {
 2     
 3     private int[] array;
 4     private int len;
 5     private int index = 0;
 6     
 7     public CircularSet(int size) {
 8         array = new int[size];
 9         len = size;
10         for (int i = 0; i < len; i++) array[i] = -1;
11     }
12     
13     public synchronized void add(int i) { 
14         array[index] = i;
15         index = (index + 1) % len;
16     }
17     
18     public synchronized boolean contains(int val) {
19         for (int i = 0; i < len; i++) {
20             if (array[i] == val) return true;
21         }
22         return false;
23     }
24 
25 }
CircularSet.java

 1 import java.util.concurrent.ExecutorService;
 2 import java.util.concurrent.Executors;
 3 
 4 
 5 public class SerialNumberChecker {
 6     
 7     private static final int SIZE = 10;
 8     private static CircularSet serials = new CircularSet(1000);
 9     private static ExecutorService exec = Executors.newCachedThreadPool();
10     
11     public static void main(String[] args) {
12         
13         for (int i = 0; i < SIZE; i++) {
14             exec.execute(new Runnable() {
15 
16                 @Override
17                 public void run() {
18                     while (true) {
19                         int serial = SerialNumberGenerator.nextSerialNumber();
20                         if (serials.contains(serial)) {
21                             System.out.println("error... " + serial + " duplicate.");
22                             System.exit(0);
23                         }
24                         serials.add(serial);
25                     }
26                 }
27                 
28             });
29         }
30     }
31 
32 }
33 
34 /**
35  * 程序执行结果:
36  * error... 8298 duplicate.
37  * error... 9049 duplicate.
38  * error... 9051 duplicate.
39  * error... 9045 duplicate.
40  * error... 9050 duplicate.
41  * error... 9048 duplicate.
42  * error... 9047 duplicate.
43  * error... 9046 duplicate.
44  */
SerialNumberChecker.java

posted @ 2013-10-20 17:50  slowalker  阅读(182)  评论(0编辑  收藏  举报