图文|Android 使用Thread 和多线程使用互斥锁
為什么需要多線程進行開發?
多線程不管是嵌入式系統RTOS,Linux,還是應用開發,中間件開發,都是必不可少的,做一個技術的時候,如果能做到舉一反三,下次使用的時候不會再遇到坑,我這次給出的例子是Android 的多線程開發。
如何使用一個線程
在Android 應用程序里面如何使用一個線程呢?直接看下面的代碼,代碼不是很多,如果需要用的話直接摘抄過去就好了。
//定義一個線程private?SendThread?mSendThread?=?null;
/**
?*?線程實體
?*/
private?class?SendThread?extends?Thread{
????public?void?run()?{
????}
}
//實例化線程
if?(mSendThread?==?null){
???mSendThread?=?new?SendThread();
}
//啟動線程
mSendThread.start();
多次調用start是同一個堆棧空間嗎?
如果只new了一次線程,多次start,會出現怎么樣的情況呢?
android 線程start的函數原型如下
public?synchronized?void?start()?{????????/**
?????????*?This?method?is?not?invoked?for?the?main?method?thread?or?"system"
?????????*?group?threads?created/set?up?by?the?VM.?Any?new?functionality?added
?????????*?to?this?method?in?the?future?may?have?to?also?be?added?to?the?VM.
?????????*
?????????*?A?zero?status?value?corresponds?to?state?"NEW".
?????????*/
????????//?Android-changed:?throw?if?'started'?is?true
????????if?(threadStatus?!=?0?||?started)
????????????throw?new?IllegalThreadStateException();
????????/*?Notify?the?group?that?this?thread?is?about?to?be?started
?????????*?so?that?it?can?be?added?to?the?group's?list?of?threads
?????????*?and?the?group's?unstarted?count?can?be?decremented.?*/
????????group.add(this);
????????started?=?false;
????????try?{
????????????nativeCreate(this,?stackSize,?daemon);
????????????started?=?true;
????????}?finally?{
????????????try?{
????????????????if?(!started)?{
????????????????????group.threadStartFailed(this);
????????????????}
????????????}?catch?(Throwable?ignore)?{
????????????????/*?do?nothing.?If?start0?threw?a?Throwable?then
??????????????????it?will?be?passed?up?the?call?stack?*/
????????????}
????????}
????????}
然后我做了下面的一個代碼實驗
/**?*?發送線程實體
?*/
private?class?SendThread?extends?Thread{
????int?testCount?=?20;
????public?void?run()?{
????????while(testCount?>0)
????????{
????????????Log.d(TAG,"testCount:"+testCount);
????????????testCount?--;
????????}
????}
}
//實例化線程
if?(mSendThread?==?null){
???mSendThread?=?new?SendThread();
}
//啟動線程
mSendThread.start();
mSendThread.start();
結果輸出如下
D/ttyusb:?testCount:20D/ttyusb:?testCount:20
D/ttyusb:?testCount:19
D/ttyusb:?testCount:19
D/ttyusb:?testCount:18
D/ttyusb:?testCount:18
D/ttyusb:?testCount:17
D/ttyusb:?testCount:16
D/ttyusb:?testCount:17
D/ttyusb:?testCount:15
D/ttyusb:?testCount:16
D/ttyusb:?testCount:14
D/ttyusb:?testCount:15
D/ttyusb:?testCount:13
D/ttyusb:?testCount:14
D/ttyusb:?testCount:12
D/ttyusb:?testCount:13
D/ttyusb:?testCount:11
D/ttyusb:?testCount:12
D/ttyusb:?testCount:10
D/ttyusb:?testCount:9
D/ttyusb:?testCount:11
D/ttyusb:?testCount:8
D/ttyusb:?testCount:10
D/ttyusb:?testCount:7
D/ttyusb:?testCount:9
D/ttyusb:?testCount:6
D/ttyusb:?testCount:5
D/ttyusb:?testCount:8
D/ttyusb:?testCount:4
D/ttyusb:?testCount:7
D/ttyusb:?testCount:3
D/ttyusb:?testCount:6
D/ttyusb:?testCount:2
D/ttyusb:?testCount:1
D/ttyusb:?testCount:5
D/ttyusb:?testCount:4
D/ttyusb:?testCount:3
D/ttyusb:?testCount:2
D/ttyusb:?testCount:1
可以看出線程每次start后,他們使用的堆棧空間是不相同的。
在雙線程里面使用互斥鎖
使用互斥鎖的情況非常普遍,但是新手寫代碼肯定會有道意想不到的問題,我就是那個新手,我就遇到了那個意想不到的問題。
給出下面一段代碼
/**?????*?線程實體
?????*/
????public?class?SendThread?extends?Thread?{
????????public?void?run()?{
????????????isStart?=?true;
????????????for(int?i=0;i<20;i++)
????????????{
????????????????lock.lock();
????????????????successCount?=?i;
????????????????lock.unlock();
????????????????Log.d(TAG,?"Write:testCount:"?+?successCount);
????????????}
????????????isStart?=?false;
????????}
????}
????/**
?????*?接收數據的線程
?????*/
????public?class?ReceiveThread?extends?Thread?{
????????
????????public?void?run()?{
????????????int?testCount?=?20;
????????????super.run();
????????????//條件判斷,只要條件為true,則一直執行這個線程
????????????while?(isStart?==?true)?{
????????????????testCount?=?successCount;
????????????????Log.d(TAG,?"Read:testCount:"?+?testCount);
????????????}
????????}
????}
代碼執行的流程大概如下
代碼輸出
03-08?11:41:35.383?14866-14866/??D/TEST:?啟動線程完成
03-08?11:41:35.383?14866-14906/??D/TEST:?Write:testCount:1
03-08?11:41:35.383?14866-14906/??D/TEST:?Write:testCount:2
03-08?11:41:35.383?14866-14907/??D/TEST:?Read:testCount:1
03-08?11:41:35.383?14866-14906/??D/TEST:?Write:testCount:3
03-08?11:41:35.383?14866-14907/??D/TEST:?Read:testCount:3
03-08?11:41:35.383?14866-14906/??D/TEST:?Write:testCount:4
03-08?11:41:35.383?14866-14907/??D/TEST:?Read:testCount:4
03-08?11:41:35.383?14866-14906/??D/TEST:?Write:testCount:5
03-08?11:41:35.383?14866-14907/??D/TEST:?Read:testCount:5
03-08?11:41:35.383?14866-14906/??D/TEST:?Write:testCount:6
03-08?11:41:35.383?14866-14907/??D/TEST:?Read:testCount:6
03-08?11:41:35.383?14866-14906/??D/TEST:?Write:testCount:7
03-08?11:41:35.383?14866-14907/??D/TEST:?Read:testCount:7
03-08?11:41:35.383?14866-14906/??D/TEST:?Write:testCount:8
03-08?11:41:35.383?14866-14907/??D/TEST:?Read:testCount:8
03-08?11:41:35.383?14866-14906/??D/TEST:?Write:testCount:9
03-08?11:41:35.383?14866-14907/??D/TEST:?Read:testCount:9
03-08?11:41:35.383?14866-14906/??D/TEST:?Write:testCount:10
03-08?11:41:35.383?14866-14907/??D/TEST:?Read:testCount:10
03-08?11:41:35.383?14866-14906/??D/TEST:?Write:testCount:11
03-08?11:41:35.383?14866-14907/??D/TEST:?Read:testCount:11
03-08?11:41:35.383?14866-14906/??D/TEST:?Write:testCount:12
03-08?11:41:35.383?14866-14907/??D/TEST:?Read:testCount:12
03-08?11:41:35.383?14866-14906/??D/TEST:?Write:testCount:13
03-08?11:41:35.383?14866-14907/??D/TEST:?Read:testCount:13
03-08?11:41:35.383?14866-14906/??D/TEST:?Write:testCount:14
03-08?11:41:35.383?14866-14907/??D/TEST:?Read:testCount:14
03-08?11:41:35.383?14866-14906/??D/TEST:?Write:testCount:15
03-08?11:41:35.383?14866-14907/??D/TEST:?Read:testCount:15
03-08?11:41:35.383?14866-14906/??D/TEST:?Write:testCount:16
03-08?11:41:35.383?14866-14907/??D/TEST:?Read:testCount:16
03-08?11:41:35.383?14866-14906/??D/TEST:?Write:testCount:17
03-08?11:41:35.383?14866-14907/??D/TEST:?Read:testCount:17
03-08?11:41:35.383?14866-14906/??D/TEST:?Write:testCount:18
03-08?11:41:35.383?14866-14907/??D/TEST:?Read:testCount:18
03-08?11:41:35.383?14866-14906/??D/TEST:?Write:testCount:19
03-08?11:41:35.383?14866-14907/??D/TEST:?Read:testCount:19
上述代碼問題
我們希望的代碼流程是,我發送一個數據,你就接收一個數據,不能出現數據丟失的情況,但是上面的日子來看,接收數據是發生了丟失,這樣的情況如果在實際應用中是非常危險的。
所以代碼需要類似下面這樣修改
????/**?????*?線程實體
?????*/
????public?class?SendThread?extends?Thread?{
????????public?void?run()?{
????????????isStart?=?true;
????????????for(int?i=0;i<20;i++)
????????????{
????????????????Log.d(TAG,"開始寫線程~");
????????????????lock.lock();
????????????????successCount?=?i;
????????????????Log.d(TAG,?"寫數據:testCount:"?+?successCount);
????????????????lock.unlock();
????????????????Log.d(TAG,"寫線程休眠~");
????????????????sendTreadSleep(10);
????????????}
????????????isStart?=?false;
????????}
????}
????/**
?????*?接收數據的線程
?????*/
????public?class?ReceiveThread?extends?Thread?{
????????@Override
????????public?void?run()?{
????????????int?testCount?=?20;
????????????super.run();
????????????//條件判斷,只要條件為true,則一直執行這個線程
????????????while?(isStart?==?true)?{
????????????????Log.d(TAG,"開始讀線程~");
????????????????lock.lock();
????????????????testCount?=?successCount;
????????????????Log.d(TAG,?"讀數據~:testCount:"?+?testCount);
????????????????lock.unlock();
????????????????Log.d(TAG,"讀線程休眠~");
????????????????receiveTreadSleep(5);
????????????}
????????}
????}
工程的Demo到時候在文末給出,寫線程在寫完后,就休眠10MS,然后再到讀線程執行,加的日志非常方便大家觀看兩個線程之間的運行邏輯。
日志輸出如下
?D/TEST:?啟動線程完成?D/TEST:?開始寫線程~
?D/TEST:?寫數據:testCount:0
?D/TEST:?寫線程休眠~
?D/TEST:?開始讀線程~
?D/TEST:?讀數據~:testCount:0
?D/TEST:?讀線程休眠~
?D/TEST:?開始讀線程~
?D/TEST:?讀數據~:testCount:0
?D/TEST:?讀線程休眠~
?D/TEST:?開始寫線程~
?D/TEST:?寫數據:testCount:1
?D/TEST:?寫線程休眠~
?D/TEST:?開始讀線程~
?D/TEST:?讀數據~:testCount:1
?D/TEST:?讀線程休眠~
?D/TEST:?開始讀線程~
?D/TEST:?讀數據~:testCount:1
?D/TEST:?讀線程休眠~
?D/TEST:?開始寫線程~
?D/TEST:?寫數據:testCount:2
?D/TEST:?寫線程休眠~
?D/TEST:?開始讀線程~
?D/TEST:?讀數據~:testCount:2
?D/TEST:?讀線程休眠~
?D/TEST:?開始讀線程~
?D/TEST:?讀數據~:testCount:2
?D/TEST:?讀線程休眠~
?D/TEST:?開始寫線程~
?D/TEST:?寫數據:testCount:3
?D/TEST:?寫線程休眠~
?D/TEST:?開始讀線程~
?D/TEST:?讀數據~:testCount:3
?D/TEST:?讀線程休眠~
?D/TEST:?開始寫線程~
?D/TEST:?開始讀線程~
?D/TEST:?寫數據:testCount:4
?D/TEST:?寫線程休眠~
?D/TEST:?讀數據~:testCount:4
?D/TEST:?讀線程休眠~
?D/TEST:?開始讀線程~
?D/TEST:?讀數據~:testCount:4
?D/TEST:?讀線程休眠~
?D/TEST:?開始寫線程~
?D/TEST:?寫數據:testCount:5
?D/TEST:?寫線程休眠~
?D/TEST:?開始讀線程~
?D/TEST:?讀數據~:testCount:5
?D/TEST:?讀線程休眠~
?D/TEST:?開始讀線程~
?D/TEST:?讀數據~:testCount:5
?D/TEST:?讀線程休眠~
?D/TEST:?開始寫線程~
?D/TEST:?寫數據:testCount:6
?D/TEST:?寫線程休眠~
?D/TEST:?開始讀線程~
?D/TEST:?讀數據~:testCount:6
?D/TEST:?讀線程休眠~
?D/TEST:?開始讀線程~
?D/TEST:?讀數據~:testCount:6
?D/TEST:?讀線程休眠~
?D/TEST:?開始寫線程~
?D/TEST:?寫數據:testCount:7
?D/TEST:?寫線程休眠~
?D/TEST:?開始讀線程~
?D/TEST:?讀數據~:testCount:7
?D/TEST:?讀線程休眠~
?D/TEST:?開始讀線程~
?D/TEST:?讀數據~:testCount:7
?D/TEST:?讀線程休眠~
?D/TEST:?開始寫線程~
?D/TEST:?寫數據:testCount:8
?D/TEST:?寫線程休眠~
?D/TEST:?開始讀線程~
?D/TEST:?讀數據~:testCount:8
?D/TEST:?讀線程休眠~
?D/TEST:?開始讀線程~
?D/TEST:?讀數據~:testCount:8
?D/TEST:?讀線程休眠~
?D/TEST:?開始寫線程~
?D/TEST:?寫數據:testCount:9
?D/TEST:?寫線程休眠~
?D/TEST:?開始讀線程~
?D/TEST:?讀數據~:testCount:9
?D/TEST:?讀線程休眠~
?D/TEST:?開始讀線程~
?D/TEST:?讀數據~:testCount:9
?D/TEST:?讀線程休眠~
?D/TEST:?開始寫線程~
?D/TEST:?寫數據:testCount:10
?D/TEST:?寫線程休眠~
?D/TEST:?開始讀線程~
?D/TEST:?讀數據~:testCount:10
?D/TEST:?讀線程休眠~
grande.ttyusb.test0001/com.evergrande.ttyusb.test0001.MainActivity]?connect:?already?connected?(cur=1?req=1)
?D/TEST:?開始讀線程~
?D/TEST:?開始寫線程~
?D/TEST:?讀數據~:testCount:10
?D/TEST:?讀線程休眠~
?D/TEST:?寫數據:testCount:11
?D/TEST:?寫線程休眠~
?D/mali_winsys:?EGLint?new_window_surface(egl_winsys_display*,?void*,?EGLSurface,?EGLConfig,?egl_winsys_surface**,?egl_color_buffer_format*,?EGLBoolean)?returns?0x3000
?D/TEST:?開始讀線程~
?D/TEST:?讀數據~:testCount:11
?D/TEST:?讀線程休眠~
?D/TEST:?開始寫線程~
?D/TEST:?寫數據:testCount:12
?D/TEST:?寫線程休眠~
?D/TEST:?開始讀線程~
?D/TEST:?讀數據~:testCount:12
使用synchronized來達到完成互斥
synchronized修飾的方法可以讓兩個方法之間完成互斥,比如寫和讀互斥,寫和寫互斥,讀和讀互斥,都可以用這個方法。
使用代碼如下
?????*?線程實體
?????*/
????public?class?SendThread?extends?Thread?{
????????public?void?run()?{
????????????isStart?=?true;
????????????for(int?i=0;i<10;i++)
????????????{
????????????????Log.d(TAG,"開始寫線程~");
????????????????mDataValue.setData(i);
????????????????Log.d(TAG,?"寫數據:testCount:"?+?successCount);
????????????????Log.d(TAG,"寫線程休眠~");
????????????????sendTreadSleep(10);
????????????}
????????????isStart?=?false;
????????}
????}
????/**
?????*?接收數據的線程
?????*/
????public?class?ReceiveThread?extends?Thread?{
????????
????????public?void?run()?{
????????????int?testCount?=?20;
????????????Log.d(TAG,"s開始讀線程~");
????????????super.run();
????????????//條件判斷,只要條件為true,則一直執行這個線程
????????????while?(isStart?==?true)?{
????????????????Log.d(TAG,"開始讀線程~");
????????????????testCount?=?mDataValue.getData();
????????????????Log.d(TAG,?"讀數據~:testCount:"?+?testCount);
????????????????Log.d(TAG,"讀線程休眠~");
????????????????receiveTreadSleep(5);
????????????}
????????}
????}
????/**
?????*?發送線程延遲
?????*?@param?millis?毫秒
?????*/
????private?void?sendTreadSleep(int?millis)
????{
????????try{
????????????mSendThread.sleep(millis);
????????}?catch?(Exception?e)?{
????????????e.printStackTrace();
????????}
????}
????/**
?????*?接收線程延遲
?????*?@param?millis?毫秒
?????*/
????private?void?receiveTreadSleep(int?millis)
????{
????????try{
????????????mReceiveThread.sleep(millis);
????????}?catch?(Exception?e)?{
????????????e.printStackTrace();
????????}
????}
????private?class?DataValue{
????????private?synchronized?void?setData(int?value){
????????????Log.d(TAG,"設置數據~setData");
????????????successCount?=?value;
????????}
????????private?synchronized??int?getData(){
????????????Log.d(TAG,"獲取數據~getData");
????????????return?successCount;
????????}
????}
讀寫鎖ReentrantReadWriteLock
上面是使用互斥鎖,這里介紹一個讀寫鎖,也是用來完成互斥的。使用代碼如下
????/**?????*?線程實體
?????*/
????public?class?SendThread?extends?Thread?{
????????public?void?run()?{
????????????isStart?=?true;
????????????for(int?i=0;i<10;i++)
????????????{
????????????????mrwDataValue.setData(i);
????????????????Log.d(TAG,Thread.currentThread().getName()?+"寫休眠");
????????????????sendTreadSleep(10);
????????????}
????????????isStart?=?false;
????????}
????}
????/**
?????*?接收數據的線程
?????*/
????public?class?ReceiveThread?extends?Thread?{
????????@Override
????????public?void?run()?{
????????????int?testCount?=?20;
????????????Log.d(TAG,"s開始讀線程~");
????????????super.run();
????????????//條件判斷,只要條件為true,則一直執行這個線程
????????????while?(isStart?==?true)?{
????????????????mrwDataValue.getData();
????????????????Log.d(TAG,Thread.currentThread().getName()?+"讀休眠");
????????????????receiveTreadSleep(5);
????????????}
????????}
????}
????/*
?????*?使用讀寫鎖完成互斥ReadWriteLock
?????*/
????private?class?rwDataValue{
????????private?ReadWriteLock?readWriteLock?=?new?ReentrantReadWriteLock();
????????private?int?Data;
????????private?void?setData(int?value){
????????????readWriteLock.writeLock().lock();
????????????try?{
????????????????Log.d(TAG,?Thread.currentThread().getName()?+"寫數據~setData:"+value);
????????????????Data?=?value;
????????????????Thread.sleep(30);
????????????}catch?(Exception?i){
????????????????Log.e(TAG,"error");
????????????}finally?{
????????????????readWriteLock.writeLock().unlock();
????????????}
????????}
????????private??void?getData(){
????????????readWriteLock.readLock().lock();
????????????try?{
????????????????Log.d(TAG,Thread.currentThread().getName()?+"獲取數據~getData:"?+Data);
????????????????Thread.sleep(10);
????????????}catch?(Exception?i){
????????????????Log.e(TAG,"error");
????????????}finally?{
????????????????readWriteLock.readLock().unlock();
????????????}
????????}
參考
https://blog.csdn.net/zy_style/article/details/53423877
Demo 代碼
https://github.com/weiqifa0/androitdThread
當你看到這里的時候,說明你已經閱讀完上面的內容
不管怎樣,感謝您有心或者無意的關注和支持
想獲取學習1024G資料,請點擊狀態欄公眾號福利按鈕
總結
以上是生活随笔為你收集整理的图文|Android 使用Thread 和多线程使用互斥锁的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何在Cadence Virtuoso中
- 下一篇: 波士顿房价数据说明