關於Java自增操做的原子性分析

        剛接觸編程的時候寫的是C/C++,轉java後,一直都認爲java自增操做是原子性的操做,昨天看到一篇關於ArrayList多線程不安全的文章,裏邊有個Demo,發現新增操做(add)次數和List的size怎麼也對不上,按照自增是原子性操做的話是應該不會出現這個問題的,但現實確實是殘酷的。幾經查看ArrayList的源碼,都沒有發現問題,因而終於懷疑到自增操做上,經驗證:Java自增操做並不是原子性操做。證實以下:

一、測試代碼

package com.hjr.back16.common.util;

import static java.lang.System.out;

/**
 * 自增操做原子性測試
 * @author scuechjr
 * @date 2016-4-24 1:29:48
 */
public class IncrementTestDemo {
	public static int count = 0;
	public static Counter counter = new Counter();
	
	public static void main(String[] args) {
		
		for (int i = 0; i < 10; i++) {
			new Thread() {
				public void run() {
					for (int j = 0; j < 1000; j++) {
						count++;
						counter.increment();
					}
				}
			}.start();
		}
		
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		out.println("static count: " + count);
		out.println("Counter: " + counter.getValue());
	}
}

class Counter {
	
	private int value;

	public synchronized int getValue() {
		return value;
	}

	public synchronized int increment() {
		return ++value;
	}

	public synchronized int decrement() {
		return --value;
	}
}

代碼執行結果: java

static count: 9898
Counter: 10000

        其中,Counter的increment方法添加了同步鎖,能夠保證原子操做。Counter的數值爲10000,說明count確實自增了10000次,若是自增是原子操做的話,count的值應該也是10000的,然而結果並非的。 編程

注意:可能有些同窗在跑的Demo時有可能跑出count等於10000,這是由於多線程沒有同時自增的狀況,多跑幾回就發現問題,或者在確認代碼沒有問題後,把count++這句代碼後邊的counter.increment()刪除後多跑幾回就能夠了 安全

二、字節碼分析

    上邊的代碼太多,換個簡單的,java代碼: 多線程


package com.hjr.back16.common.util;

/**
 * 
 * @author scuechjr
 * @date 2016-4-24 1:56:27
 */
public class TestDemo {
	public static int count;
	
	public void code() {
		count++;
	}
}


    code()方法對應字節碼以下: 測試


public void code();
    flags: ACC_PUBLIC

    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field count:I count++開始
         3: iconst_1      
         4: iadd          
         5: putstatic     #2                  // Field count:I count++結束
         8: return        
      LineNumberTable:
        line 12: 0
        line 13: 8
        如上字節碼,咱們發現自增操做包括取數(getstatic  #2)、加一(iconst_1和iadd)、保存(putstatic  #2),並非咱們認爲的一條機器指令搞定的。
相關文章
相關標籤/搜索