j**a執行緒的兩個特性:
可見性和有序性
多個執行緒之間是不能傳遞資料互動的,他們之間的互動只能通過共享變數來實現。在多個執行緒之間共享了count類的乙個物件,這個物件時被建立在主記憶體(堆記憶體)每個執行緒都有自己的工作記憶體(執行緒棧)工作記憶體儲存了主記憶體count物件的乙個副本,當執行緒操作count物件時,首先從主記憶體複製count物件到工作記憶體中,然後執行**count.count()該變了num值,最後用工作記憶體count重新整理主記憶體count。當乙個物件在多個記憶體中都存在副本時,如果乙個記憶體修改了共享變數,其他執行緒也應該能夠看到被修改後的值,此為可見性。
由上述可知,乙個賦值操作並不是乙個原子性操作,多個執行緒執行時,cpu對執行緒的排程是隨機的,我們不知道當前程式被執行到哪步就切換到了下乙個執行緒,乙個最經典的例子就是銀行匯款問題,乙個銀行賬戶存款100,這時乙個人從該賬戶取10元,同時另乙個人向該賬戶匯10元,那麼餘額應該還是100。那麼此時可能發生這種情況,a執行緒負責取款,b執行緒負責匯款,a從出記憶體讀到100,b從主記憶體讀到100,a執行減10操作,並將資料重新整理到主記憶體,這時主記憶體資料100-10=90,而b記憶體執行加10操作,並將資料重新整理到主記憶體,最後主記憶體資料100+10=110,顯然這是乙個嚴重的問題,我們要保證a執行緒和b執行緒有序執行,先取款後匯款或者先匯款後取款,此為有序性。
多執行緒: 執行緒是指程序中的乙個執行流程。
執行緒與程序的區別:每個程序都需要作業系統為其分配獨立的記憶體位址空間,而同一程序中的執行緒在同一塊位址空間中工作,這些執行緒可以共享同一塊記憶體和系統資源。
乙個執行緒只能被啟動一次。第二次啟動時將會丟擲j**a.lang.illegalthreadexcetpion異常
執行緒的五種狀態:
1、新建狀態(new):用new語句建立的執行緒處於新建狀態,此時它和其它的j**a物件一樣,僅僅在堆中分配了記憶體
2、就緒狀態(runnable):當乙個執行緒建立了以後,其它的執行緒呼叫它的start()方法,該執行緒就進入了就緒狀態。處於這個狀態的執行緒位於可執行池中,等待獲得cpu的使用權
3、執行狀態(running):處於這個狀態的執行緒占用cpu,執行程式的**
4、阻塞狀態(blocked):當執行緒處於阻塞狀態時,j**a虛擬機器不會給執行緒分配cpu,直到執行緒從新進入就緒狀態,它才有機會轉到執行狀態
1)位於物件等待池中的阻塞狀態:當執行緒執行時,如果執行了某個物件的wait()方法,j**a虛擬機器就會把執行緒放到這個物件的等待池中。
2)位於物件鎖中的阻塞狀態,當執行緒處於執行狀態時,試圖獲得某個物件的同步鎖時,如果該物件的同步鎖已經被其他執行緒占用,jvm就會把這個執行緒放到這個物件的鎖池中。
3)其他的阻塞狀態:當執行緒執行了sleep()方法,或者呼叫了其他執行緒的join()方法,或者發出了i/o請求時,就會進入這個狀態中。
5、死亡狀態(dead):當執行緒退出了run()方法,就進入了死亡狀態,該執行緒結束了生命週期。或者正常退出或者遇到異常退出
thread類的isalive()方法判斷乙個執行緒是否還活著,當執行緒處於死亡狀態或者新建狀態時,該方法返回false,在其餘狀態下,該方法返回true。
執行緒排程模型:
分時排程模型和搶占排程模型
jvm採用搶占排程模型
所謂的多執行緒的併發執行,其實是指巨集觀上看,各個執行緒輪流獲得cpu的使用權,分別執行各自的任務。(執行緒的排程不是跨平台,它不僅取決於j**a虛擬機器,它還依賴於作業系統)
如果希望明確地讓乙個執行緒給另外乙個執行緒執行的機會,可以採取以下的辦法之一
1、 調整各個執行緒的優先順序
2、 讓處於執行狀態的執行緒呼叫thread.sleep()方法
3、 讓處於執行狀態的執行緒呼叫thread.yield()方法
4、 讓處於執行狀態的執行緒呼叫另乙個執行緒的join()方法
調整各個執行緒的優先順序
thread類的setpriority(int)和getpriority()方法分別用來設定優先順序和讀取優先順序。
如果希望程式能夠移值到各個作業系統中,應該確保在設定執行緒的優先順序時,只使用max_priority、norm_priority、min_priority這3個優先順序
執行緒睡眠:當執行緒在執行中執行了sleep()方法時,它就會放棄cpu,轉到阻塞狀態。
執行緒讓步:當執行緒在執行中執行了thread類的yield()靜態方法時,如果此時具有相同優先順序的其它執行緒處於就緒狀態,那麼yield()方法將把當前執行的執行緒放到執行池中並使另乙個執行緒執行。如果沒有相同優先順序的可執行執行緒,則yield()方法什麼也不做。
sleep()方法和yield()方法都是thread類的靜態方法,都會使當前處於執行狀態的執行緒放棄cpu,把執行機會讓給別的執行緒,兩者的區別在於:
1、sleep()方法會給其他執行緒執行的機會,而不考慮其他執行緒的優先順序,因此會給較低執行緒乙個執行的機會;yield()方法只會給相同優先順序或者更高優先順序的執行緒乙個執行的機會。
2、當執行緒執行了sleep(long millis)方法後,將轉到阻塞狀態,引數millis指定睡眠時間;當執行緒執行了yield()方法後,將轉到就緒狀態。
3、sleep()方法宣告丟擲interruptedexception異常,而yield()方法沒有宣告丟擲任何異常
4、sleep()方法比yield()方法具有更好的移植性
等待其它執行緒的結束:join()
當前執行的執行緒可以呼叫另乙個執行緒的 join()方法,當前執行的執行緒將轉到阻塞狀態,直到另乙個執行緒執行結束,它才恢復執行。
定時器timer:在jdk的j**a.util包中提供了乙個實用類timer, 它能夠定時執行特定的任務。
執行緒的同步
原子操作:根據j**a規範,對於基本型別的賦值或者返回值操作,是原子操作。但這裡的基本資料型別不包括long和double, 因為jvm看到的基本儲存單位是32位,而long 和double都要用64位來表示。所以無法在乙個時鐘週期內完成。
自增操作(++)不是原子操作,因為它涉及到一次讀和一次寫。
原子操作:由一組相關的操作完成,這些操作可能會操縱與其它的執行緒共享的資源,為了保證得到正確的運算結果,乙個執行緒在執行原子操作其間,應該採取其他的措施使得其他的執行緒不能操縱共享資源。
同步**塊:為了保證每個執行緒能夠正常執行原子操作,j**a引入了同步機制,具體的做法是在代表原子操作的程式**前加上synchronized標記,這樣的**被稱為同步**塊。
同步鎖:每個j**a物件都有且只有乙個同步鎖,在任何時刻,最多隻允許乙個執行緒擁有這把鎖。
當乙個執行緒試圖訪問帶有synchronized(this)標記的**塊時,必須獲得 this關鍵字引用的物件的鎖,在以下的兩種情況下,本執行緒有著不同的命運。
1、 假如這個鎖已經被其它的執行緒占用,jvm就會把這個執行緒放到本物件的鎖池中。本執行緒進入阻塞狀態。鎖池中可能有很多的執行緒,等到其他的執行緒釋放了鎖,jvm就會從鎖池中隨機取出乙個執行緒,使這個執行緒擁有鎖,並且轉到就緒狀態。
2、 假如這個鎖沒有被其他執行緒占用,本執行緒會獲得這把鎖,開始執行同步**塊。
(一般情況下在執行同步**塊時不會釋放同步鎖,但也有特殊情況會釋放物件鎖
如在執行同步**塊時,遇到異常而導致執行緒終止,鎖會被釋放;在執行**塊時,執行了鎖所屬物件的wait()方法,這個執行緒會釋放物件鎖,進入物件的等待池中)
執行緒同步的特徵:
1、如果乙個同步**塊和非同步**塊同時操作共享資源,仍然會造成對共享資源的競爭。因為當乙個執行緒執行乙個物件的同步**塊時,其他的執行緒仍然可以執行物件的非同步**塊。(所謂的執行緒之間保持同步,是指不同的執行緒在執行同乙個物件的同步**塊時,因為要獲得物件的同步鎖而互相牽制)
2、 每個物件都有唯一的同步鎖
3、 在靜態方法前面可以使用synchronized修飾符。
4、 當乙個執行緒開始執行同步**塊時,並不意味著必須以不間斷的方式執行,進入同步**塊的執行緒可以執行thread.sleep()或者執行thread.yield()方法,此時它並不釋放物件鎖,只是把執行的機會讓給其他的執行緒。
5、 synchronized宣告不會被繼承,如果乙個用synchronized修飾的方法被子類覆蓋,那麼子類中這個方法不在保持同步,除非用synchronized修飾。
執行緒安全的類:
1、 這個類的物件可以同時被多個執行緒安全的訪問。
2、 每個執行緒都能正常的執行原子操作,得到正確的結果。
3、 在每個執行緒的原子操作都完成後,物件處於邏輯上合理的狀態。
釋放物件的鎖:
1、 執行完同步**塊就會釋放物件的鎖
2、 在執行同步**塊的過程中,遇到異常而導致執行緒終止,鎖也會被釋放
3、 在執行同步**塊的過程中,執行了鎖所屬物件的wait()方法,這個執行緒會釋放物件鎖,進入物件的等待池。
死鎖 當乙個執行緒等待由另乙個執行緒持有的鎖,而後者正在等待已被第乙個執行緒持有的鎖時,就會發生死鎖。jvm不監測也不試圖避免這種情況,因此保證不發生死鎖就成了程式設計師的責任。
如何避免死鎖
乙個通用的經驗法則是:當幾個執行緒都要訪問共享資源a、b、c 時,保證每個執行緒都按照同樣的順序去訪問他們。
執行緒通訊
j**a.lang.object類中提供了兩個用於執行緒通訊的方法
1、 wait():執行了該方法的執行緒釋放物件的鎖,jvm會把該執行緒放到物件的等待池中。該執行緒等待其它執行緒喚醒
2、 notify():執行該方法的執行緒喚醒在物件的等待池中等待的乙個執行緒,jvm從物件的等待池中隨機選擇乙個執行緒,把它轉到物件的鎖池中。
執行緒預設優先順序是5,thread類中有三個常量,定義執行緒優先順序範圍:static int max_priority執行緒可以具有最高優先順序
static int min_priority執行緒可以具有最低優先順序
static int norm_priority預設優先順序
多執行緒知識點總結
多執行緒的問題主要圍繞3個問題處理 1.原子性,2.可見性,3.有序性 1.原子性,不可被其他執行緒打斷的操作。如read.write sychronized 2.可見性 一條執行緒修改了某值,新值對其他執行緒立即可知 普通變數是通過主記憶體完成多執行緒的共享,因此在多執行緒的情況下,很多髒資料。v...
執行緒知識點
1.join 執行緒a 呼叫了 執行緒b 的 join 方法 執行緒a 必須等待執行緒b 執行完後 才能執行 2.yield sleep wait notify 釋放鎖問題 呼叫yield sleep 執行緒 持有的鎖 不釋放 wait 呼叫方法前 必須持有鎖,呼叫之後釋放鎖,wait 返回後持有鎖...
多執行緒 知識點總結二
1.sleep 和wait 方法的區別?sleep 必須指時間 不釋放鎖。wait 可以不指定時間,也可以指定時間 釋放鎖。2.為什麼wait notify notifyall 等方法都定義在object類中?wait 等待,notify 喚醒單個執行緒,notifyall 喚醒所有的執行緒 這些方...