Java多線程共享數據、同步、通信
一、線程共享數據
a)繼承Thread,那么我們可以創建很多個這樣的類,但是每個這樣的類都是相互不關聯的,也就是說我們Thread類中的內容每個創建出來的類都有一份,因此它不適合作為數據共享的線程來操作。同時由于Java繼承的唯一性,我們只能繼承一個對象。
b)使用runnable就可以解決唯一性和不能共享的問題(不是說使用runnable就解決了共享問題,只是相對于創建Thread來說,它可以算的上是共享了,為了獲得更精確的共享問題,它必須的使用線程同步操作)。實現了runnable接口的類比較適合用作共享數據。
一個測試例子à證明runnable能實現數據共享,thread不能
Thread_thread一個繼承了Thread的線程
Thread_runnable是一個時間了runnable的接口,他們在run里面有共同的方法
for(int i=0;i<20;i++){ |
輸入了三組321321321
因為創建的是三個對象,每一個對象都擁有自己的一個備份
將一個runnable作為參數,實例化三個thread對象
thread_runnable ru=new thread_runnable(); |
輸入了32133
雖然說著不是完整意義上的數據共享,但是相當于上述打印三組完整的數據來說,它已經實現了數據共享,我們從中也可以看到,我們只創建了一個runnable對象(數據只產生了一份),它由三個Thread調用。
新建三個runnable對象,分別給每一個thread傳遞
Thread th1=new Thread(new thread_runnable()); |
打印結果是321321321
我們可以看到我們產生了三個runnable對象,每一個都有自己的一份使用
綜上所述:只有將一個runnable對象作為參數,傳遞給thread對象才能實現數據共享。
注意:當我們創建一個Thread對象,并多次調用start方法的時候,系統是不會給你創建多個Thread線程的,它只會運行那個唯一的Thread一次而已,也就是說你運行了一次start方法之后再調用一個它的start方法是沒有意義的(那個Thread沒有結束的情況下),系統不會給你多次運行的。
二、線程同步
a)線程代碼塊(在代碼中添加Synchronized(對象){})
i.Synchronized(對象),每個對象都有個標志位,當我們進入synchronized代碼塊中,系統就讓這個對象的標志位變為0,就相當于給這個對象添加上了一把鎖,當別的代碼運行到這個代碼塊的時候因為加了鎖,所以不能進去,當第一個程序它運行出去之后,系統就會讓標志位變為1,相當于解鎖。這樣別的代碼又可以訪問了。從而實現同步(安全)操作。
ii.當我們將我們的標志位對象放在run方法里面定義的時候,我們是不能實現同步的,因為我們每次運行一個線程,都將調用它的run方法,從而每次都會創建一個新的標志位對象,也就是說我們所有的run方法都含有自己的一個標志位對象,因此不能實現加鎖的過程。一般都是放在runnable接口中進行定義的。
b)線程方法(在代碼的方法申明中public和void之間添加synchronized)
i.每次只能有一個線程調用這個同步方法,而且每次這個方法都得運行完,這就是同步代碼方法。
ii.同步方法默認使用的是this來作為標志對象位的,這個this就是我們的當前類。
c)注意:
i.當一個同步代碼塊和一個同步代碼方法使用的不是同一個對象作為標志位的時候,它們就不會實現同步,這也就是數,當兩個同步代碼塊不使用同一個對象作為標志位,那他們就不能實現同步。
ii.調用線程的Start方法的時候,并沒有真正的運行這個代碼,而只是說這個代碼已經準備就緒,有運行的可能。
三、線程通信
a)當我們的代碼中使用了synchronized(對象)同步代碼塊的時候,如果我們想實現線程通信,也就是如果我們想使用wait、notify或者notifyall時,我們必須在靜態代碼塊中使用對象.wait()、對象.notify()、對象.notifyAll()來通信,不然的話講會報Illegal的錯誤。
b)Notify是喚醒同一監視器下(相當于同一個標志位對象)的第一個wait線程,而notifyall是喚醒所有的處于同一監視器下的(同一標志位對象)的線程。