Android開發之利用ConditionVariable同步執行緒結果
概述
在我們日常開發當中有時會遇到這樣的場景,例如一個操作依賴2個數據,而這2個數據是通過2個執行緒分別計算得到的,而這個2個計算結果完成的先後順序是不能事先確定的,那我們就得想辦法同步這2個執行緒的結果。
解決方案
方案1
使用在各個工作執行緒都檢查執行條件是否滿足需求的方式。
1:為這個2個執行緒操作分別定義一個flag
,用來指示這個2個執行緒是否完成了自己的任務.
2:在需要使用這個2個執行緒的結果的那個方法裡面判斷這個2個flag
是否同時為真,同時為真說明我們已經得到了所有依賴的資料了,執行相應的操作即可。
3:最後就是必須在每個執行緒中呼叫這個方法,因為我們不知道哪個執行緒先完成任務,所以需要在每個工作執行緒中檢查。
方案2
使用執行緒同步技術,我們此處使用Android 提供的ConditionVariable來同步執行緒。
1:為執行緒1操作定義一個用於指示任務是否完成的flag
2:線上程2中在完成自己的工作後檢查執行緒1的flag
,如果為真說明兩個執行緒都完成了自己的工作。如果執行緒1的flag為假則表示,執行緒1還沒有完成自己的工作,所以執行緒2需要等待其完成,此處就是使用ConditionVariable
來完成的。
3:執行緒1完成了自己的工作後,就會解除執行緒2的等待狀態,繼續執行最後的任務。
ConditionVariable示例
基本知識
ConditionVariable提供的一種依據條件變數鎖定的機制。本身非常簡單,只有4個公有方法。
Method | ReturnType | Description |
---|---|---|
block() | void | Block the current thread until the condition is opened. |
block(long timeout) | boolean | Block the current thread until the condition is opened or until timeout milliseconds have passed. |
close() | void | Reset the condition to the closed state. |
open() | void | Open the condition, and release all threads that are blocked. |
在處於close
狀態的情況下,我們可以使用block()
來阻塞執行緒,使用open()
來開啟執行緒。
程式碼示例
假設我們現在有兩個工作執行緒,每個工作執行緒耗時不能事先確定,而只有當著兩個執行緒的工作都完成後我們才能將我們想要的結果顯示在一個TextView
上。
如下圖所示:
public class MainActivity extends AppCompatActivity {
private volatile String t1Des="t1",t2Des="t2";
private volatile boolean firstThreadFinish=false;
private TextView tvResult;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvResult=findViewById(R.id.showResult);
findViewById(R.id.btnStartWork).setOnClickListener(clickListener);
}
private void startWork(){
final ConditionVariable completed=new ConditionVariable();
new Thread(new Runnable() {
@Override
public void run() {
try {
long t=1000+new Random().nextInt(5)*1000;
Thread.sleep(t);
t1Des=String.format("執行緒:%s 耗時:%s",Thread.currentThread().getName(),t);
firstThreadFinish=true;
completed.open();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
long t=1000+new Random().nextInt(5)*1000;
Thread.sleep(t);
t2Des=String.format("執行緒:%s 耗時:%s",Thread.currentThread().getName(),t);
try {
if (firstThreadFinish==false)
completed.block();
MainActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
allFinished();
}
});
} finally {
completed.open();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
private void allFinished(){
tvResult.setText(t1Des+"\n\n"+t2Des);
}
private View.OnClickListener clickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btnStartWork:
tvResult.setText("正在計算...");
firstThreadFinish=false;
startWork();
break;
default:
break;
}
}
};
}
我們在第二個執行緒中使用firstThreadFinish
這個flag來判斷第一個執行緒的工作是否已經完成,如果沒有完成則鎖定第二個執行緒,等待第一個執行緒。
if (firstThreadFinish==false)
completed.block();
當在第一執行緒完成了自己的工作後,就將firstThreadFinish
設定為真,並且解除第二個執行緒的鎖定狀態。
firstThreadFinish=true;
completed.open();
這樣我們就達到了同步兩個執行緒結果的目的。
總結
ConditionVariable
是一種簡單的執行緒鎖定機制,其在處理兩個執行緒的情況下比較方便,當將上面的工作執行緒擴充套件到10個,那我們就需要9個ConditionVariable
來完成,那樣就非常複雜了。
當出現多個執行緒需要同步的情況,最為簡單的還是迴圈等待,例如
while(!(t1Flag&&t2Flag&&t3Flag...)){
Thread.sleap(200);
}
技術無好壞,關鍵看是否合適當前情況。