鍍金池/ 教程/ Android/ SMP(Symmetric Multi-Processor) Primer for Android
檢測常用的手勢
優(yōu)化layout的層級
用戶輸入
管理應(yīng)用的內(nèi)存
聯(lián)系人信息
開發(fā)輔助程序
Android多媒體
添加語音功能
顯示位置地址
提供向下與橫向?qū)Ш?/span>
支持游戲控制器
訪問可穿戴數(shù)據(jù)層
處理多點觸控手勢
全屏沉浸式應(yīng)用
為多線程創(chuàng)建管理器
數(shù)據(jù)保存
Intent的發(fā)送
更新Notification
優(yōu)化下載以高效地訪問網(wǎng)絡(luò)
打印
打包可穿戴應(yīng)用
接收從其他App傳送來的數(shù)據(jù)
發(fā)送與接收消息
建立靈活動態(tài)的UI
處理鍵盤輸入
Building a Work Policy Controller
建立測試環(huán)境
創(chuàng)建表盤
分享文件
顯示Notification進度
實現(xiàn)自適應(yīng)UI流(Flows)
使用設(shè)備管理策略增強安全性
使用能感知版本的組件
執(zhí)行網(wǎng)絡(luò)操作
建立文件分享
添加移動
更新你的Security Provider來對抗SSL漏洞利用
支持鍵盤導(dǎo)航
創(chuàng)建和監(jiān)視地理圍欄
發(fā)送并同步數(shù)據(jù)
使用BigView樣式
無線連接設(shè)備
提供向上導(dǎo)航與歷史導(dǎo)航
最小化定期更新造成的影響
實現(xiàn)向下的導(dǎo)航
支持不同的屏幕大小
Android 可穿戴應(yīng)用
添加動畫
顯示聯(lián)系人頭像
使用OpenGL ES顯示圖像
處理輸入法可見性
分享文件
保持設(shè)備喚醒
淡化系統(tǒng)Bar
使用NFC分享文件
保存到Preference
Android聯(lián)系人信息與位置信息
創(chuàng)建標準的網(wǎng)絡(luò)請求
使用Drawables
管理Bitmap的內(nèi)存使用
管理Activity的生命周期
按需加載視圖
傳輸資源
為可穿戴設(shè)備創(chuàng)建自定義UI
在一個線程中執(zhí)行一段特定的代碼
性能優(yōu)化
隱藏導(dǎo)航欄
創(chuàng)建目錄瀏覽器
為多種大小的屏幕進行規(guī)劃
View間漸變
使用觸摸手勢
高效加載大圖
使用CursorLoader在后臺加載數(shù)據(jù)
創(chuàng)建抽屜式導(dǎo)航(navigation drawer)
管理音頻焦點
創(chuàng)建后臺服務(wù)
創(chuàng)建功能測試
創(chuàng)建使用Material Design的應(yīng)用
停止與重啟Activity
添加一個簡便的分享功能
啟動Activity時保留導(dǎo)航
TV應(yīng)用清單
創(chuàng)建向后兼容的UI
?# 優(yōu)化自定義View
創(chuàng)建單元測試
在UI上顯示Bitmap
建立OpenGL ES的環(huán)境
構(gòu)建表盤服務(wù)
JNI Tips
建立搜索界面
實現(xiàn)自定義View的繪制
使用HTTPS與SSL
按需操控BroadcastReceiver
分享簡單的數(shù)據(jù)
繪制形狀
Android位置信息
創(chuàng)建并運行可穿戴應(yīng)用
執(zhí)行 Sync Adpater
獲取最后可知位置
創(chuàng)建 Android 項目
實現(xiàn)高效的導(dǎo)航
退出全屏的Activity
創(chuàng)建Card
兼容音頻輸出設(shè)備
同步數(shù)據(jù)單元
傳輸數(shù)據(jù)時避免消耗大量電量
保存到文件
緩存Bitmap
提供配置 Activity
調(diào)度重復(fù)的鬧鐘
實現(xiàn)輔助功能
重復(fù)的下載是冗余的
隱藏狀態(tài)欄
實現(xiàn)自定義的網(wǎng)絡(luò)請求
規(guī)劃界面和他們之間的關(guān)系
使用Sync Adapter傳輸數(shù)據(jù)
TV應(yīng)用內(nèi)搜索
響應(yīng)觸摸事件
使用Google Cloud Messaging(已廢棄)
控制相機
Android網(wǎng)絡(luò)連接與云服務(wù)
請求分享一個文件
處理TV硬件
響應(yīng)UI可見性的變化
使用網(wǎng)絡(luò)服務(wù)發(fā)現(xiàn)
指定輸入法類型
優(yōu)化電池壽命
創(chuàng)建TV應(yīng)用
獲取聯(lián)系人列表
拖拽與縮放
啟動與停止線程池中的線程
創(chuàng)建 Sync Adpater
使用 WiFi P2P 服務(wù)發(fā)現(xiàn)
開始使用Material Design
代理至新的APIs
使用include標簽重用layouts
使得View可交互
高效顯示Bitmap
創(chuàng)建企業(yè)級應(yīng)用
Fragments之間的交互
創(chuàng)建與執(zhí)行測試用例
綜合:設(shè)計我們的樣例 App
繪制表盤
建立簡單的用戶界面
自定義動畫
開發(fā)輔助服務(wù)
避免出現(xiàn)程序無響應(yīng)ANR(Keeping Your App Responsive)
使用ViewPager實現(xiàn)屏幕滑動
設(shè)計高效的導(dǎo)航
Android分享操作(Building Apps with Content Sharing)
提供向后的導(dǎo)航
保持向下兼容
創(chuàng)建TV播放應(yīng)用
縮放View
使用 WiFi 建立 P2P 連接
Android后臺任務(wù)
連接到網(wǎng)絡(luò)
為 Notification 添加頁面
使TV應(yīng)用是可被搜索的
添加Action Bar
使用Material的主題
啟動另一個Activity
顯示正在播放卡片
適配不同的系統(tǒng)版本
輕松錄制視頻
創(chuàng)建可穿戴的應(yīng)用
創(chuàng)建自定義的布局
重新創(chuàng)建Activity
使用CursorLoader執(zhí)行查詢?nèi)蝿?wù)
使用舊的APIs實現(xiàn)新API的效果
使用備份API
安全要點
Android入門基礎(chǔ):從這里開始
保存并搜索數(shù)據(jù)
根據(jù)網(wǎng)絡(luò)連接類型來調(diào)整下載模式
使用Tabs創(chuàng)建Swipe視圖
SMP(Symmetric Multi-Processor) Primer for Android
解析 XML 數(shù)據(jù)
使用 Volley 傳輸網(wǎng)絡(luò)數(shù)據(jù)
建立ActionBar
Android交互設(shè)計
使用Intent修改聯(lián)系人信息
增加搜索功能
輕松拍攝照片
定義形狀
測試你的Activity
在 Notifcation 中接收語音輸入
與其他應(yīng)用的交互
管理系統(tǒng)UI
追蹤手勢移動
Android界面設(shè)計
執(zhí)行 Android 程序
顯示確認界面
創(chuàng)建Lists與Cards
打印HTML文檔
創(chuàng)建TV應(yīng)用
為多屏幕設(shè)計
定義Shadows與Clipping視圖
使用Fragment建立動態(tài)UI
接收Activity返回的結(jié)果
布局變更動畫
定位常見的問題
自定義ActionBar的風(fēng)格
定義Layouts
發(fā)送簡單的網(wǎng)絡(luò)請求
啟動與銷毀Activity
與UI線程通信
非UI線程處理Bitmap
創(chuàng)建TV布局
提升Layout的性能
報告任務(wù)執(zhí)行狀態(tài)
判斷并監(jiān)測網(wǎng)絡(luò)連接狀態(tài)
兼容不同的設(shè)備
處理按鍵動作
優(yōu)化性能和電池使用時間
給其他App發(fā)送簡單的數(shù)據(jù)
Implementing App Restrictions
向后臺服務(wù)發(fā)送任務(wù)請求
展示Card翻轉(zhuǎn)動畫
管理ViewGroup中的觸摸事件
兼容不同的屏幕密度
通過藍牙進行調(diào)試
為可穿戴設(shè)備創(chuàng)建Notification
控制音量與音頻播放
獲取聯(lián)系人詳情
在表盤上顯示信息
提供向上的導(dǎo)航
滾動手勢動畫
幫助用戶在TV上找到內(nèi)容
創(chuàng)建TV導(dǎo)航
為索引指定App內(nèi)容
ActionBar的覆蓋疊加
Android Wear 上的位置檢測
保護安全與隱私的最佳策略
Ensuring Compatibility with Managed Profiles
解決云同步的保存沖突
獲取位置更新
創(chuàng)建List
測試程序
管理網(wǎng)絡(luò)的使用情況
為App內(nèi)容開啟深度鏈接
推薦TV內(nèi)容
建立一個Notification
管理音頻播放
設(shè)計表盤
拍照
處理控制器輸入動作
判斷并監(jiān)測設(shè)備的底座狀態(tài)與類型
處理查詢的結(jié)果
保存到數(shù)據(jù)庫
支持多個游戲控制器
創(chuàng)建 Stub Content Provider
使得ListView滑動順暢
處理數(shù)據(jù)層的事件
創(chuàng)建TV應(yīng)用的第一步
使得你的App內(nèi)容可被Google搜索
將 Notification 放成一疊
創(chuàng)建 Stub 授權(quán)器
暫停與恢復(fù)Activity
管理設(shè)備的喚醒狀態(tài)
Android圖像與動畫
打印照片
云同步
創(chuàng)建TV直播應(yīng)用
為Notification賦加可穿戴特性
提供一個Card視圖
建立請求隊列(RequestQueue)
適配不同的語言
創(chuàng)建詳情頁
測試UI組件
接收其他設(shè)備的文件
創(chuàng)建自定義View
建立第一個App
創(chuàng)建2D Picker
監(jiān)測電池的電量與充電狀態(tài)
打印自定義文檔
抽象出新的APIs
通知提示用戶
獲取文件信息
運用投影與相機視角
在IntentService中執(zhí)行后臺任務(wù)
多線程操作
創(chuàng)建一個Fragment
添加Action按鈕
在不同的 Android 系統(tǒng)版本支持控制器
維護兼容性
發(fā)送文件給其他設(shè)備
創(chuàng)建TV游戲應(yīng)用
創(chuàng)建自定義的View類
代碼性能優(yōu)化建議
Intent過濾
適配不同的屏幕

SMP(Symmetric Multi-Processor) Primer for Android

編寫:kesenhoo - 原文:http://developer.android.com/training/articles/smp.html

從Android 3.0開始,系統(tǒng)針對多核CPU架構(gòu)的機器做了優(yōu)化支持。這份文檔介紹了針對多核系統(tǒng)應(yīng)該如何編寫C,C++以及Java程序。這里只是作為Android應(yīng)用開發(fā)者的入門教程,并不會深入討論這個話題,并且我們會把討論范圍集中在ARM架構(gòu)的CPU上。

如果你并沒有時間學(xué)習(xí)整篇文章,你可以跳過前面的理論部分,直接查看實踐部分。但是我們并不建議這樣做。

簡要介紹

SMP 的全稱是“Symmetric Multi-Processor”。 它表示的是一種雙核或者多核CPU的設(shè)計架構(gòu)。在幾年前,所有的Android設(shè)備都還是單核的。

大多數(shù)的Android設(shè)備已經(jīng)有了多個CPU,但是通常來說,其中一個CPU負責(zé)執(zhí)行程序,其他的CPU則處理設(shè)備硬件的相關(guān)事務(wù)(例如,音頻)。這些CPU可能有著不同的架構(gòu),運行在上面的程序無法在內(nèi)存中彼此進行溝通交互。

目前大多數(shù)售賣的Android設(shè)備都是SMP架構(gòu)的,這使得軟件開發(fā)者處理問題更加復(fù)雜。對于多線程的程序,如果多個線程執(zhí)行在不同的內(nèi)核上,這會使得程序更加容易發(fā)生race conditions。 更糟糕的是,基于ARM架構(gòu)的SMP比起x86架構(gòu)來說,更加復(fù)雜,更難進行處理。那些在x86上測試通過的程序可能會在ARM上崩潰。

下面我們會介紹為何會這樣以及如何做才能夠使得你的代碼行為正常。

1)理論篇

這里會快速并且簡要的介紹這個復(fù)雜的主題。其中一些部分并不完整,但是并沒有出現(xiàn)錯誤或者誤導(dǎo)。

查看文章末尾的[進一步閱讀]()可以了解這個主題的更多知識。

1.1)內(nèi)存一致性模型(Memory consistency models)

內(nèi)存一致性模型(Memory consistency models)通常也被叫做“memory models”,描述了硬件架構(gòu)如何確保內(nèi)存訪問的一致性。例如,如果你對地址A進行了一個賦值,然后對地址B也進行了賦值,那么內(nèi)存一致性模型就需要確保每一個CPU都需要知道剛才的操作賦值與操作順序。

這個模型通常被程序員稱為:順序一致性(sequential consistency), 請從文章末尾的進一步閱讀查看Adve & Gharachorloo這篇文章。

  • 所有的內(nèi)存操作每次只能執(zhí)行一個。
  • 所有的操作,在單核CPU上,都是順序執(zhí)行的。

如果你關(guān)注一段代碼在內(nèi)存中的讀寫操作,在sequentially-consistent的CPU架構(gòu)上,是按照期待的順序執(zhí)行的。It’s possible that the CPU is actually reordering instructions and delaying reads and writes, but there is no way for code running on the device to tell that the CPU is doing anything other than execute instructions in a straightforward manner. (We’re ignoring memory-mapped device driver I/O for the moment.)

To illustrate these points it’s useful to consider small snippets of code, commonly referred to as litmus tests. These are assumed to execute in program order, that is, the order in which the instructions appear here is the order in which the CPU will execute them. We don’t want to consider instruction reordering performed by compilers just yet.

Here’s a simple example, with code running on two threads:

Thread 1 Thread 2 A = 3 B = 5 reg0 = B reg1 = A

Thread 1 Thread 2
A = 3 B = 5 reg0 = B reg1 = A

In this and all future litmus examples, memory locations are represented by capital letters (A, B, C) and CPU registers start with “reg”. All memory is initially zero. Instructions are executed from top to bottom. Here, thread 1 stores the value 3 at location A, and then the value 5 at location B. Thread 2 loads the value from location B into reg0, and then loads the value from location A into reg1. (Note that we’re writing in one order and reading in another.)

Thread 1 and thread 2 are assumed to execute on different CPU cores. You should always make this assumption when thinking about multi-threaded code.

Sequential consistency guarantees that, after both threads have finished executing, the registers will be in one of the following states:

Registers States
reg0=5, reg1=3 possible (thread 1 ran first)
reg0=0, reg1=0 possible (thread 2 ran first)
reg0=0, reg1=3 possible (concurrent execution)
reg0=5, reg1=0 never

To get into a situation where we see B=5 before we see the store to A, either the reads or the writes would have to happen out of order. On a sequentially-consistent machine, that can’t happen.

Most uni-processors, including x86 and ARM, are sequentially consistent. Most SMP systems, including x86 and ARM, are not.

1.1.1)Processor consistency

1.1.2)CPU cache behavior

1.1.3)Observability

1.1.4)ARM’s weak ordering

1.2)Data memory barriers

1.2.1)Store/store and load/load

1.2.2)Load/store and store/load

1.2.3)Barrier instructions

1.2.4)Address dependencies and causal consistency

1.2.5)Memory barrier summary

1.3)Atomic operations

1.3.1)Atomic essentials

1.3.2)Atomic + barrier pairing

1.3.3)Acquire and release

2)實踐篇

調(diào)試內(nèi)存一致性(memory consistency)的問題非常困難。如果內(nèi)存柵欄(memory barrier)導(dǎo)致一些代碼讀取到陳舊的數(shù)據(jù),你將無法通過調(diào)試器檢查內(nèi)存dumps文件來找出原因。By the time you can issue a debugger query, the CPU cores will have all observed the full set of accesses, and the contents of memory and the CPU registers will appear to be in an “impossible” state.

2.1)What not to do in C

2.1.1)C/C++ and “volatile”

2.1.2)Examples

2.2)在Java中不應(yīng)該做的事

我們沒有討論過Java語言的一些相關(guān)特性,因此我們首先來簡要的看下那些特性。

2.2.1)Java中的"synchronized"與"volatile"關(guān)鍵字

“synchronized”關(guān)鍵字提供了Java一種內(nèi)置的鎖機制。每一個對象都有一個相對應(yīng)的“monitor”,這個監(jiān)聽器可以提供互斥的訪問。

“synchronized”代碼段的實現(xiàn)機制與自旋鎖(spin lock)有著相同的基礎(chǔ)結(jié)構(gòu): 他們都是從獲取到CAS開始,以釋放CAS結(jié)束。這意味著編譯器(compilers)與代碼優(yōu)化器(code optimizers)可以輕松的遷移代碼到“synchronized”代碼段中。一個實踐結(jié)果是:你不能判定synchronized代碼段是執(zhí)行在這段代碼下面一部分的前面,還是這段代碼上面一部分的后面。更進一步,如果一個方法有兩個synchronized代碼段并且鎖住的是同一個對象,那么在這兩個操作的中間代碼都無法被其他的線程所檢測到,編譯器可能會執(zhí)行“鎖粗化lock coarsening”并且把這兩者綁定到同一個代碼塊上。

另外一個相關(guān)的關(guān)鍵字是“volatile”。在Java 1.4以及之前的文檔中是這樣定義的:volatile聲明和對應(yīng)的C語言中的一樣可不靠。從Java 1.5開始,提供了更有力的保障,甚至和synchronization一樣具備強同步的機制。

volatile的訪問效果可以用下面這個例子來說明。如果線程1給volatile字段做了賦值操作,線程2緊接著讀取那個字段的值,那么線程2是被確保能夠查看到之前線程1的任何寫操作。更通常的情況是,任何線程對那個字段的寫操作對于線程2來說都是可見的。實際上,寫volatile就像是釋放件監(jiān)聽器,讀volatile就像是獲取監(jiān)聽器。

非volatile的訪問有可能因為照顧volatile的訪問而需要做順序的調(diào)整。例如編譯器可能會往上移動一個非volatile加載操作,但是不會往下移動。Volatile之間的訪問不會因為彼此而做出順序的調(diào)整。虛擬機會注意處理如何的內(nèi)存柵欄(memory barriers)。

當(dāng)加載與保存大多數(shù)的基礎(chǔ)數(shù)據(jù)類型,他們都是原子的atomic, 對于long以及double類型的數(shù)據(jù)則不具備原子型,除非他們被聲明為volatile。即使是在單核處理器上,并發(fā)多線程更新非volatile字段值也還是不確定的。

2.2.2)Examples

下面是一個錯誤實現(xiàn)的單步計數(shù)器(monotonic counter)的示例: (Java theory and practice: Managing volatility).

class Counter {
    private int mValue;

    public int get() {
        return mValue;
    }
    public void incr() {
        mValue++;
    }
}

假設(shè)get()與incr()方法是被多線程調(diào)用的。然后我們想確保當(dāng)get()方法被調(diào)用時,每一個線程都能夠看到當(dāng)前的數(shù)量。最引人注目的問題是mValue++實際上包含了下面三個操作。

  1. reg = mValue
  2. reg = reg + 1
  3. mValue = reg

如果兩個線程同時在執(zhí)行incr()方法,其中的一個更新操作會丟失。為了確保正確的執(zhí)行++的操作,我們需要把incr()方法聲明為“synchronized”。這樣修改之后,這段代碼才能夠在單核多線程的環(huán)境中正確的執(zhí)行。

然而,在SMP的系統(tǒng)下還是會執(zhí)行失敗。不同的線程通過get()方法獲取到得值可能是不一樣的。因為我們是使用通常的加載方式來讀取這個值的。我們可以通過聲明get()方法為synchronized的方式來修正這個錯誤。通過這些修改,這樣的代碼才是正確的了。

不幸的是,我們有介紹過有可能發(fā)生的鎖競爭(lock contention),這有可能會傷害到程序的性能。除了聲明get()方法為synchronized之外,我們可以聲明mValue“volatile”. (請注意incr()必須使用synchronize) 現(xiàn)在我們知道volatile的mValue的寫操作對于后續(xù)的讀操作都是可見的。incr()將會稍稍有點變慢,但是get()方法將會變得更加快速。因此讀操作多于寫操作時,這會是一個比較好的方案。(請參考AtomicInteger.)

下面是另外一個示例,和之前的C示例有點類似:

class MyGoodies {
    public int x, y;
}
class MyClass {
    static MyGoodies sGoodies;

    void initGoodies() {    // runs in thread 1
        MyGoodies goods = new MyGoodies();
        goods.x = 5;
        goods.y = 10;
        sGoodies = goods;
    }

    void useGoodies() {    // runs in thread 2
        if (sGoodies != null) {
            int i = sGoodies.x;    // could be 5 or 0
            ....
        }
    }
}

這段代碼同樣存在著問題,sGoodies = goods的賦值操作有可能在goods成員變量賦值之前被察覺到。如果你使用volatile聲明sGoodies變量,你可以認為load操作為atomic_acquire_load(),并且把store操作認為是atomic_release_store()

(請注意僅僅是sGoodies的引用本身為volatile,訪問它的內(nèi)部字段并不是這樣的。賦值語句z = sGoodies.x會執(zhí)行一個volatile load MyClass.sGoodies的操作,其后會伴隨一個non-volatile的load操作::sGoodies.x。如果你設(shè)置了一個本地引用MyGoodies localGoods = sGoodies, z = localGoods.x,這將不會執(zhí)行任何volatile loads.)

另外一個在Java程序中更加常用的范式就是臭名昭著的“double-checked locking”:

class MyClass {
    private Helper helper = null;

    public Helper getHelper() {
        if (helper == null) {
            synchronized (this) {
                if (helper == null) {
                    helper = new Helper();
                }
            }
        }
        return helper;
    }
}

上面的寫法是為了獲得一個MyClass的單例。我們只需要創(chuàng)建一次這個實例,通過getHelper()這個方法。為了避免兩個線程會同時創(chuàng)建這個實例。我們需要對創(chuàng)建的操作加synchronize機制。然而,我們不想要為了每次執(zhí)行這段代碼的時候都為“synchronized”付出額外的代價,因此我們僅僅在helper對象為空的時候加鎖。

在單核系統(tǒng)上,這是不能正常工作的。JIT編譯器會破壞這件事情。請查看4)Appendix的“‘Double Checked Locking is Broken’ Declaration”獲取更多的信息, 或者是Josh Bloch’s Effective Java書中的Item 71 (“Use lazy initialization judiciously”)。

在SMP系統(tǒng)上執(zhí)行這段代碼,引入了一個額外的方式會導(dǎo)致失敗。把上面那段代碼換成C的語言實現(xiàn)如下:

if (helper == null) {
    // acquire monitor using spinlock
    while (atomic_acquire_cas(&this.lock, 0, 1) != success)
        ;
    if (helper == null) {
        newHelper = malloc(sizeof(Helper));
        newHelper->x = 5;
        newHelper->y = 10;
        helper = newHelper;
    }
    atomic_release_store(&this.lock, 0);
}

此時問題就更加明顯了: helper的store操作發(fā)生在memory barrier之前,這意味著其他的線程能夠在store x/y之前觀察到非空的值。

你應(yīng)該嘗試確保store helper執(zhí)行在atomic_release_store()方法之后。通過重新排序代碼進行加鎖,但是這是無效的,因為往上移動的代碼,編譯器可以把它移動回原來的位置:在atomic_release_store()前面。 (這里沒有讀懂,下次再回讀)

有2個方法可以解決這個問題:

  • 刪除外層的檢查。這確保了我們不會在synchronized代碼段之外做任何的檢查。
  • 聲明helper為volatile。僅僅這樣一個小小的修改,在前面示例中的代碼就能夠在Java 1.5及其以后的版本中正常工作。

下面的示例演示了使用volatile的2各重要問題:

class MyClass {
    int data1, data2;
    volatile int vol1, vol2;

    void setValues() {    // runs in thread 1
        data1 = 1;
        vol1 = 2;
        data2 = 3;
    }

    void useValues1() {    // runs in thread 2
        if (vol1 == 2) {
            int l1 = data1;    // okay
            int l2 = data2;    // wrong
        }
    }
    void useValues2() {    // runs in thread 2
        int dummy = vol2;
        int l1 = data1;    // wrong
        int l2 = data2;    // wrong
    }

請注意useValues1(),如果thread 2還沒有察覺到vol1的更新操作,那么它也無法知道data1或者data2被設(shè)置的操作。一旦它觀察到了vol1的更新操作,那么它也能夠知道data1的更新操作。然而,對于data2則無法做任何猜測,因為store操作是在volatile store之后發(fā)生的。

useValues2()使用了第2個volatile字段:vol2,這會強制VM生成一個memory barrier。這通常不會發(fā)生。為了建立一個恰當(dāng)?shù)摹癶appens-before”關(guān)系,2個線程都需要使用同一個volatile字段。在thread 1中你需要知道vol2是在data1/data2之后被設(shè)置的。(The fact that this doesn’t work is probably obvious from looking at the code; the caution here is against trying to cleverly “cause” a memory barrier instead of creating an ordered series of accesses.)

2.3)What to do

2.3.1)General advice

在C/C++中,使用pthread操作,例如mutexes與semaphores。他們會使用合適的memory barriers,在所有的Android平臺上提供正確有效的行為。請確保正確這些技術(shù),例如在沒有獲得對應(yīng)的mutex的情況下賦值操作需要很謹慎。

避免直接使用atomic方法。如果locking與unlocking之間沒有競爭,locking與unlocking一個pthread mutex 分別需要一個單獨的atomic操作。如果你需要一個lock-free的設(shè)計,你必須在開始寫代碼之前了解整篇文檔的要點。(或者是尋找一個已經(jīng)為SMP ARM設(shè)計好的庫文件)。

Be extremely circumspect with "volatile” in C/C++. It often indicates a concurrency problem waiting to happen.

In Java, the best answer is usually to use an appropriate utility class from the java.util.concurrent package. The code is well written and well tested on SMP.

Perhaps the safest thing you can do is make your class immutable. Objects from classes like String and Integer hold data that cannot be changed once the class is created, avoiding all synchronization issues. The book Effective Java, 2nd Ed. has specific instructions in “Item 15: Minimize Mutability”. Note in particular the importance of declaring fields “final" (Bloch).

If neither of these options is viable, the Java “synchronized” statement should be used to guard any field that can be accessed by more than one thread. If mutexes won’t work for your situation, you should declare shared fields “volatile”, but you must take great care to understand the interactions between threads. The volatile declaration won’t save you from common concurrent programming mistakes, but it will help you avoid the mysterious failures associated with optimizing compilers and SMP mishaps.

The Java Memory Model guarantees that assignments to final fields are visible to all threads once the constructor has finished — this is what ensures proper synchronization of fields in immutable classes. This guarantee does not hold if a partially-constructed object is allowed to become visible to other threads. It is necessary to follow safe construction practices.(Safe Construction Techniques in Java).

2.3.2)Synchronization primitive guarantees

The pthread library and VM make a couple of useful guarantees: all accesses previously performed by a thread that creates a new thread are observable by that new thread as soon as it starts, and all accesses performed by a thread that is exiting are observable when a join() on that thread returns. This means you don’t need any additional synchronization when preparing data for a new thread or examining the results of a joined thread.

Whether or not these guarantees apply to interactions with pooled threads depends on the thread pool implementation.

In C/C++, the pthread library guarantees that any accesses made by a thread before it unlocks a mutex will be observable by another thread after it locks that same mutex. It also guarantees that any accesses made before calling signal() or broadcast() on a condition variable will be observable by the woken thread.

Java language threads and monitors make similar guarantees for the comparable operations.

2.3.3)Upcoming changes to C/C++

The C and C++ language standards are evolving to include a sophisticated collection of atomic operations. A full matrix of calls for common data types is defined, with selectable memory barrier semantics (choose from relaxed, consume, acquire, release, acq_rel, seq_cst).

See the Further Reading section for pointers to the specifications.

3)Closing Notes

While this document does more than merely scratch the surface, it doesn’t manage more than a shallow gouge. This is a very broad and deep topic. Some areas for further exploration:

  • Learn the definitions of happens-before, synchronizes-with, and other essential concepts from the Java Memory Model. (It’s hard to understand what “volatile” really means without getting into this.)
  • Explore what compilers are and aren’t allowed to do when reordering code. (The JSR-133 spec has some great examples of legal transformations that lead to unexpected results.)
  • Find out how to write immutable classes in Java and C++. (There’s more to it than just “don’t change anything after construction”.)
  • Internalize the recommendations in the Concurrency section of Effective Java, 2nd Edition. (For example, you should avoid calling methods that are meant to be overridden while inside a synchronized block.)
  • Understand what sorts of barriers you can use on x86 and ARM. (And other CPUs for that matter, for example Itanium’s acquire/release instruction modifiers.)
  • Read through the java.util.concurrent and java.util.concurrent.atomic APIs to see what's available.
  • Consider using concurrency annotations like @ThreadSafe and @GuardedBy (from net.jcip.annotations).

The Further Reading section in the appendix has links to documents and web sites that will better illuminate these topics.

4)Appendix

4.1)SMP failure example

4.2)Implementing synchronization stores

4.3)Further reading