Java/多執行緒與同步

維基教科書,自由的教學讀本
跳至導覽 跳至搜尋

對於多執行緒,Java提供了豐富且強健的支持。在原始碼方面,Java提供了synchronized關鍵字,對具體一個對象實現執行緒獨占,完成所謂的原子操作。在程序方面,Java提供的多執行緒支持主要體現在Object和Thread兩個類上,可以看出,Java從開始就視每一個類實例是生存在多執行緒的環境當中的。

多執行緒[編輯]

當你用java命令行開啟一個程序的時候,java就運行在一個進程之上了。所謂進程是相對於作業系統來說的,一個程序被作業系統所啟動,就是運行了一個進程。但在一個進程內部,因為程序的需要,可以同時運行幾個執行緒。注意,這裡的同時的意思是,處理器按時間分配,在不同的時間段運行不同的執行緒,但具體一個時間點,肯定只能由一個執行緒存在,而具體的時間分配對於程序是無關的。與進程不同,執行緒享有共同的堆空間,全局空間,函數等等,但每一個執行緒擁有自己的棧空間。因為執行緒享有共同的堆空間,所以會形成在同一時間不同執行緒對同一個對象的操作。對於那些沒有完成的操作,可能由於別的執行緒的打擾而出現錯誤,所以必須實現所謂的原子操作,即在同一時間,一個操作只能由一個執行緒來運行,只有當該執行緒完成了操作以後,別的執行緒才可能進入。為了實現原子操作,就必須在執行緒將要進入該操作的時候申明對操作的控制權。

Java利用synchronized關鍵字來申明對一個對象的排他佔有,該關鍵字可以用在函數頭中,也可以在函數體的一段中聲明,具體使用如下

// 使用在函数头中
public class Processor {
    public synchronized operate(){
        // 该函数为原子操作
    }
}

以上的函數說明operate操作為一個原子操作,即每次只能由一個執行緒進入該函數中運行,而該類的實例為所謂的「控制權」。也就是說,執行緒在運行該程序的時候始終獨占該類的實例,導致別的執行緒無法進入。接下來的,在函數體中的申明將更好的能看清這一點。

// 使用在函数中
public class Processor {
    public operate(){
        synchronized(obj) {        
            // 该块内为原子操作
        }
    }
}

以上的函數聲明了一個程序塊為原子操作,而所謂的「控制權」聲明在括號里,即obj。也就是說,在執行緒進入該塊前會查看是否有別的執行緒佔有該obj,如果沒有,該執行緒即可進入該塊中運行,並且同時擁有該塊的控制權,否則,該執行緒將被掛起,直到在該塊中運行的執行緒退出該塊,該執行緒才能繼續進行。當執行緒退出該塊後,執行緒自動失去對obj的獨占權,別的執行緒才可得以進入。但是,換句話說,在synchronized申明的函數或是程序塊中,同時只能存在一個執行緒。下來補充說明一個特殊情況:

// 使用在函数中
public class Processor {
    public operate(){
        synchronized(obj) {
            obj = new Object();        
            // 该块内不是原子操作
        }
    }
}

在執行緒進入到synchronized的塊中後,obj即被替換,所以現在如有新的執行緒想進入該程序塊時,因為新的obj沒有被占用,所以可以進入,該程序塊失去了原子操作的功能。

Object類對多執行緒的支持[編輯]

與多執行緒相關的Object成員函數有如下一些:

  1. void notify()
  2. void notifyAll()
  3. void wait()
  4. void wait(long timeout)
  5. void wait(long timeout, int nanos)

其中,如果執行緒調用wait函數,則該執行緒則失去對該類實例的控制權,並且將自己掛起,掛起時間由輸入參數而定。比如,當一個執行緒運行在一個原子操作的時候,調用wait函數,就可以不用退出該程序塊而讓別的執行緒有機會進入該原子操作。但如果wait的時間到了,卻有別的執行緒還在該程序塊中運行當中,則該執行緒繼續掛起,直到佔有該程序塊的執行緒退出為止才能繼續運行。

Thread類的相關函數[編輯]