Java中的多線程
------- android培訓、java培訓、期待與您交流! ----------
首先,在開篇講線程之前要說一個問題,我們知道多線程的執行原理是cpu在不同的線程中做著切換操作,而一提到多線程,大家首先想到的肯定是提高系統的運行效率,可是真的是這樣的么?我們來借助一位大神博客中的代碼就可以看出來有時單線程的運行效率要高于多線程:
import threadingfrom time import ctimeclass MyThread(threading.Thread): def __init__(self, func, args, name): threading.Thread.__init__(self) self.name = name self.func = func self.args = args def run(self):程序的運行結果就是單線程比多線程的運行效率有著明顯的提升,可這是為什么呢?還是因為線程的執行原理,是因為cpu在你的各個線程之間做著快速的切換操作,那么如果你是單核的cpu那么你的程序就沒有必要驗證多線程了,因為在各個線程之間的切換都是在一個cpu下執行的,只有在雙核或多核的cpu才可以以不同的cpu來執行不同的線程來提高程序的運行效率。相信很多人在自己的電腦上實驗過多線程的執行時間問題,對于得出的結果疑惑不已,其實就是因為你的cpu的原因了。
好了,提了一個小的問題,現在我們切入主題,說一下java中的線程。線程有兩種兩種創建方式,一種是直接繼承Thread類并且覆蓋其run()方法,但是我們知道java只支持單繼承,那么如果我們的類還有一個父類,那么就難以實現這個方法。第二種是通過聲明實現Runnable接口,然后實現run方法,然后可以分配該類的實例,在創建Thread類的時候,通過帶參構造來傳遞并啟動,Runnable為非Thread類提供了一種激活方式。那么下面我們就用代碼演示一下第二種創建線程的方式:
class Show implements Runnable { private int num = 100; @Override public void run() { while (true) { if (num > 0) { System.out.println(Thread.currentThread().getName()+":"+num); num--; } else { break; } } }}public class MyMain { public static void main(String[] args) { Show show = new Show(); Thread t = new Thread(show); Thread t1 = new Thread(show); Thread t2 = new Thread(show); Thread t3 = new Thread(show); t.start(); t1.start(); t2.start(); t3.start(); }}這樣就完成了四個線程在同時執行循環,直到num見到0為止,可是如果我們開他的運行結果,會發現一個很明顯的問題:
這是我截圖了一部分運行結果,可以明顯的發現出現了四次100,那么這是為什么呢?就拿這段代碼打個比方,當你的t線程拿到cpu的執行權時,執行內部的run方法,進入循環判定if條件,判定num>0通過,進入內部代碼塊打印出num的值,可是就在這個時候,另外一個線程t1獲得了執行權,同樣進入循環判定if條件當判定num>0通過時,num的值并未改變,可是卻依舊打印出num的值,這樣就出現了兩個相同的值,同樣的當有多個這樣的狀況出現時,就會出現多個重復的值,這就是線程的安全問題,也就是當多個線程同時操作同一個variable,就可能會出現不可預知的結果。那么該怎樣解決這樣的問題呢?這就用到了synchronized(同步函數),為線程加鎖,相當于不管哪一個線程(例如線程A),運行到這個方法時,都要檢查有沒有其它線程B(或者C、 D等)正在用這個方法(或者該類的其他同步方法),有的話要等正在使用synchronized方法的線程B(或者C 、D)運行完這個方法后再運行此線程A,沒有的話,鎖定調用者,然后直接運行。可能光靠說的很難理解,下面還是展示一段上面代碼的修改版,然后我們再來看看運行結果:
class Show implements Runnable { private int num = 100; Object obj = new Object(); @Override public void run() { while (true) { synchronized (obj) { if (num > 0) { System.out.println(Thread.currentThread().getName()+":"+num); num--; } else { break; } } } }}public class MyMain { public static void main(String[] args) { Show show = new Show(); Thread t = new Thread(show); Thread t1 = new Thread(show); Thread t2 = new Thread(show); Thread t3 = new Thread(show); t.start(); t1.start(); t2.start(); t3.start(); }}這次再看代碼的運行結果時,我們會發現剛剛的重復問題不見了,而且程序雖然還是在各個線程之間切換,可是打印出來num的順序卻是固定的有序的了,就像單線程的循環一樣100到1,這就是線程的安全問題,其實解決的問題很簡單,就是在需要判定并且改變值的代碼外部加上synchronized塊,他會在你的線程進入該塊的時候做一個判定,如果有正在使用該方法的線程,如果有的話,會攔截該線程,直到正在使用synchronized方法的線程失去執行權的時候,才會再運行該線程。沒有使用的線程,執行線程并鎖定線程。在使用synchronized時需要注意死鎖問題。
新聞熱點
疑難解答