昨晚在看死鎖相關的東西,看到了synchronized
關鍵字,然后就想寫一篇關于synchronized
關鍵字的博客,就去仔細看了看相關的東西。synchronized
關鍵字在使用多線程的時候使用,于是復習一下多線程的東西,寫篇博客加深一下印象。 java中實現多線程的方式有兩種,一是繼承Thread
類,二是實現Runnable
接口,先看繼承Thread
類。
Thread
類 Thread
是java.lang
包中的一個類,實現了Runnable
接口,Runnable
接口源碼如下,簡單粗暴。
ClassExtendsThread
類,繼承了Thread
類,在run
方法中依次遞減打印出10到1。
main
函數代碼如下:
運行結果如下:
Thread-0--->10Thread-1--->10Thread-0--->9Thread-1--->9Thread-0--->8Thread-1--->8Thread-0--->7Thread-1--->7Thread-0--->6Thread-1--->6Thread-0--->5Thread-0--->4Thread-1--->5Thread-0--->3Thread-0--->2Thread-1--->4Thread-0--->1Thread-1--->3Thread-1--->2Thread-1--->1 從結果中可以看出,兩個線程都啟動起來,并且交叉運行,但是這兩個線程可以認為他們是不關聯的,因為線程1和線程2各自擁有了新的num
變量,即這兩個線程不共享該變量。接下來看實現Runnable
接口的多線程Demo。
Runnable
接口上面已經看了Runnable接口的源碼,直接上Demo。
public class ClassImplRunnable implements Runnable { private int num = 10; @Override public void run() { for (int i = 0; i < 10; i++) { if (num > 0) { // 因為Runnable中沒有currentThread方法,所以要加上Thread System.out.println(Thread.currentThread().getName() + "--->" + num--); } } }} ClassImplRunnable
和ClassExtendsThread
代碼幾乎一樣,不同的是實現了Runnable
接口。測試代碼如下:
這里的ClassImplRunnable
只實現了Runnable
接口,但是沒有start
方法,無法啟動線程,但是Thread
類中有個構造方法:
傳入一個實現Runnable
接口的類即可創建一個新的線程,然后調用start
方法即可啟動線程。注意啟動線程只能用start
方法,如果調用run
方法,那么就只是執行了一個方法,并沒有啟動線程。 運行結果如下:
兩個線程交叉運行,因為沒有使用同步鎖。同時,這里兩個線程使用了同一個對象構造了Thread
,所以其實兩個線程運行的時候共享了num
變量。 仔細回顧一下兩次運行,發現繼承Thread
類的ClassExtendsThread
類運行時,是直接創建了兩個ClassExtendsThread
對象,但是ClassImplRunnable
類是創建了一個對象,但是創建了兩個Thread
對象并啟動線程,那么就不能說繼承Thread
的類實現多線程不能共享資源,實現了Runnable
接口的多線程才能共享資源。 接下里來模仿一下ClassImplRunnable
類啟動線程的方式,代碼如下:
運行結果如下:
Thread-2--->10Thread-1--->9Thread-1--->7Thread-1--->6Thread-1--->5Thread-1--->4Thread-1--->3Thread-1--->2Thread-1--->1Thread-2--->8 結果可以看出,其實是共享了資源(num)的。 這里線程名字變了,因為new了一個ClassExtendsThread
對象的時候,就已經創建了一個線程,而后面調用Thread
的構造函數又創建了兩個線程,而且只啟動了線程2和3,即Thread-1
、Thread-2
。 今天下午查了一下午資料,也沒有一個講的清楚的好文章,最后靈光一閃,用Google搜了一下,結果直接搜出來兩篇講的很好的文章,一個是GitHub的,一個是博客園的,按道理說百度和必應都能搜出來的,但是。。。
用Thread
和Runnable
都可以實現多線程,但是一般情況下還是用實現Runnable
接口的方法來實現多線程,原因有以下幾點: 1. 因為Java的單繼承特性,如果繼承Thread
的話就不能再繼承其他類,而接口是可以實現多個的,所以實現Runnable
接口更加靈活; 2. 關于很多文章提到的資源共享問題,繼承了Thread
類的類的對象,已經是一個線程了,是可以直接調用start
方法來啟動線程的,但是如果要實現類似于ClassImplRunnable
這樣的所謂的“資源共享”,還需要把對象傳入Thread
的構造函數重新實例化Thread
對象,比較麻煩,所以還是用實現Runnable
接口的方法便捷一點。
新聞熱點
疑難解答