Android多线程研究(8)——Java中的原子性理解
一、什么是原子性
原子性是世界上最小單位,具有不可分割性。比如a=0;(a非long和double類型)這個操作是不可分割的,那么我們說這個操作是原子操作。再比如:a++;這個操作實際上是a=a+1;是可分割的,所以他不是一個原子操作。
二、原子操作的作用
非原子操作都會存在線程安全問題,需要我們使用同步技術(sychronized)來讓它變成一個原子操作。一個操作是原子操作,那么我們就稱它具有原子性。java.util.concurrent.atomic包下提供了一些原子類如下:三、理解AtomicInteger類
下面是AtomicInteger類中幾個常用的方法:可能很多人和我一樣存在著這樣一個疑問,在AtomicInteger中是如何實現原子性的呢?有前輩已經對AtomiInteger的源碼進行了分析《對 AtomicInteger 源碼 的理解》我在這里僅僅是引用一下,做個記錄。先來看看源碼: public final int getAndIncrement() {for (;;) {int current = get();int next = current + 1;if (compareAndSet(current, next))return current;}}看這段代碼和上面對原子性的分析,從int next ?= current + 1可以看到,這里很難保證操作的原子性,重點在于compareAndSet(current, next)方法
該函數 只有兩個參數,可操作的確實三個值 ,即 value ,expect, update. 他使用了 由硬件保證其原子性的指令 CAS (compare and swap)。
compareAndSet? 函數保證了 比較,賦值這兩步操作可以通過一個原子操作完成。
然后看整個函數, 所有代碼被放到了一個循環里面, 如果compareAndSet()執行失敗,則說明 在int current = get(); 以后,其他線程對value進行了更新, 于是就循環一次,重新獲取當前值,直到compareAndSet()執行成功為止。
綜上,getAndIncrement() 方法并不是原子操作。 只是保證了他和其他函數對 value 值得更新都是有效的。他所利用的是基于沖突檢測的樂觀并發策略。 可以想象,這種樂觀在線程數目非常多的情況下,失敗的概率會指數型增加。
四、理解volatile修飾符
在Java內存模型中,有main memory,每個線程也有自己的memory (例如寄存器)。為了性能,一個線程會在自己的memory中保持要訪問的變量的副本。這樣就會出現同一個變量在某個瞬間,在一個線程的memory中的值可能與另外一個線程memory中的值,或者main memory中的值不一致的情況。?
一個變量聲明為volatile,就意味著這個變量是隨時會被其他線程修改的,因此不能將它cache在線程memory中。以下例子展現了volatile的作用:?
Volatile一般情況下不能代替sychronized,因為volatile不能保證操作的原子性,即使只是i++,實際上也是由多個原子操作組成:read i; inc; write i,假如多個線程同時執行i++,volatile只能保證他們操作的i是同一塊內存,但依然可能出現寫入臟數據的情況。
volatile關鍵字用于聲明簡單類型變量,如int、float、 boolean等數據類型。如果這些簡單數據類型聲明為volatile,對它們的操作就會變成原子級別的。但這有一定的限制。例如,下面的例子中的n就不是原子級別的:?
package com.codeing.snail.test;public class TestAtomic extends Thread {public static volatile int n = 0;public void run() {for (int i = 0; i < 10; i++)try {n = n + 1;sleep(3); // 為了使運行結果更隨機,延遲3毫秒} catch (Exception e) {}}public static void main(String[] args) throws Exception {Thread threads[] = new Thread[100];for (int i = 0; i < threads.length; i++)// 建立100個線程threads[i] = new TestAtomic();for (int i = 0; i < threads.length; i++)// 運行剛才建立的100個線程threads[i].start();for (int i = 0; i < threads.length; i++)// 100個線程都執行完后繼續threads[i].join();System.out.println(" n= " + TestAtomic.n);} }如果對n的操作是原子級別的,最后輸出的結果應該為n=1000,而在執行上面積代碼時,很多時侯輸出的n都小于1000,這說明n=n+1不是原子級別的操作。原因是聲明為volatile的簡單變量如果當前值由該變量以前的值相關,那么volatile關鍵字不起作用。五、鎖和Atomic的使用場景
JDK的文檔中說:“設計原子類主要用作各種塊,用于實現非阻塞數據結構和相關基礎結構類。compareAndSet()方法不是鎖定的常規替換方法。僅當對象的重要更新限于單個變量時才應用它”與鎖相比,另外Volatile 變量是一種非常簡單但同時又非常脆弱的同步機制,它在某些情況下將提供優于鎖的性能和伸縮性。如果嚴格遵循 volatile 的使用條件 —— 即變量真正獨立于其他變量和自己以前的值 —— 在某些情況下可以使用?volatile?代替?synchronized?來簡化代碼。然而,使用?volatile?的代碼往往比使用鎖的代碼更加容易出錯。
轉載于:https://www.cnblogs.com/lanzhi/p/6468797.html
總結
以上是生活随笔為你收集整理的Android多线程研究(8)——Java中的原子性理解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 怎么重启mysql服务?(开放mysql
- 下一篇: 电饭煲保温一天危险吗