『壹』 java課程培訓機構分享Java多線程問題總結
Java多線程分類中寫了21篇多線程的文章,21篇文章的內容很多,個人認為,學習,內容越多、越雜的知識,越需要進行深刻的總結,這樣才能記憶深刻,將知識變成自己的。java課程培訓機構http://www.kmbdqn.com/認為這篇文章主要是對多線程的問題進行總結的,因此羅列了多個多線程的問題。
這些多線程的問題,有些來源於各大網站、有些來源於自己的思考。
(1)發揮多核CPU的優勢
隨著工業的進步,現在的筆記本、台式機乃至商用的應用伺服器至少也都是雙核的,4核、8核甚至16核的也都不少見,如果是單線程的程序,那麼在雙核CPU上就浪費了50%,在4核CPU上就浪費了75%。單核CPU上所謂的」多線程」那是假的多線程,同一時間處理器只會處理一段邏輯,只不過線程之間切換得比較快,看著像多個線程」同時」運行罷了。多核CPU上的多線程才是真正的多線程,它能讓你的多段邏輯同時工作,多線程,可以真正發揮出多核CPU的優勢來,達到充分利用CPU的目的。
(2)防止阻塞
從程序運行效率的角度來看,單核CPU不但不會發揮出多線程的優勢,反而會因為在單核CPU上運行多線程導致線程上下文的切換,而降低程序整體的效率。但是單核CPU我們還是要應用多線程,就是為了防止阻塞。試想,如果單核CPU使用單線程,那麼只要這個線程阻塞了,比方說遠程讀取某個數據吧,對端遲遲未返回又沒有設置超時時間,那麼你的整個程序在數據返回回來之前就停止運行了。多線程可以防止這個問題,多條線程同時運行,哪怕一條線程的代碼執行讀取數據阻塞,也不會影響其它任務的執行。
(3)便於建模
這是另外一個沒有這么明顯的優點了。假設有一個大的任務A,單線程編程,那麼就要考慮很多,建立整個程序模型比較麻煩。但是如果把這個大的任務A分解成幾個小任務,任務B、任務C、任務D,分別建立程序模型,並通過多線程分別運行這幾個任務,那就簡單很多了。
『貳』 什麼是線程阻塞
線程A等待線程B的數據,線程B等待線程A的數據,互相等待,就會陷入阻塞,這也是一種線程阻塞。
阻塞狀態是正在運行的線程遇到某個特殊情況。例如,延遲、掛起、等待I/O操作完成等。 進入阻塞狀態的線程讓出CPU,並暫時停止自己的執行。線程進入阻塞狀態後,就一直等待,直到引起阻塞的原因被消除,線程又轉入就緒狀態,重新進入就緒隊列排隊。
『叄』 java多線程開發的同步機制有哪些
Java同步
標簽: 分類:
一、關鍵字:
thread(線程)、thread-safe(線程安全)、intercurrent(並發的)
synchronized(同步的)、asynchronized(非同步的)、
volatile(易變的)、atomic(原子的)、share(共享)
二、總結背景:
一次讀寫共享文件編寫,嚯,好傢伙,竟然揪出這些零碎而又是一路的知識點。於是乎,Google和翻閱了《Java參考大全》、《Effective Java Second Edition》,特此總結一下供日後工作學習參考。
三、概念:
1、 什麼時候必須同步?什麼叫同步?如何同步?
要跨線程維護正確的可見性,只要在幾個線程之間共享非 final 變數,就必須使用 synchronized(或 volatile)以確保一個線程可以看見另一個線程做的更改。
為了在線程之間進行可靠的通信,也為了互斥訪問,同步是必須的。這歸因於java語言規范的內存模型,它規定了:一個線程所做的變化何時以及如何變成對其它線程可見。
因為多線程將非同步行為引進程序,所以在需要同步時,必須有一種方法強制進行。例如:如果2個線程想要通信並且要共享一個復雜的數據結構,如鏈表,此時需要
確保它們互不沖突,也就是必須阻止B線程在A線程讀數據的過程中向鏈表裡面寫數據(A獲得了鎖,B必須等A釋放了該鎖)。
為了達到這個目的,java在一個舊的的進程同步模型——監控器(Monitor)的基礎上實現了一個巧妙的方案:監控器是一個控制機制,可以認為是一個
很小的、只能容納一個線程的盒子,一旦一個線程進入監控器,其它的線程必須等待,直到那個線程退出監控為止。通過這種方式,一個監控器可以保證共享資源在
同一時刻只可被一個線程使用。這種方式稱之為同步。(一旦一個線程進入一個實例的任何同步方法,別的線程將不能進入該同一實例的其它同步方法,但是該實例
的非同步方法仍然能夠被調用)。
錯誤的理解:同步嘛,就是幾個線程可以同時進行訪問。
同步和多線程關系:沒多線程環境就不需要同步;有多線程環境也不一定需要同步。
鎖提供了兩種主要特性:互斥(mutual exclusion) 和可見性(visibility)。
互斥即一次只允許一個線程持有某個特定的鎖,因此可使用該特性實現對共享數據的協調訪問協議,這樣,一次就只有一個線程能夠使用該共享數據。
可見性要更加復雜一些,documents它必須確保釋放鎖之前對共享數據做出的更改對於隨後獲得該鎖的另一個線程是可見的 —— 如果沒有同步機制提供的這種可見性保證,線程看到的共享變數可能是修改前的值或不一致的值,這將引發許多嚴重問題
小結:為了防止多個線程並發對同一數據的修改,所以需要同步,否則會造成數據不一致(就是所謂的:線程安全。如java集合框架中Hashtable和
Vector是線程安全的。我們的大部分程序都不是線程安全的,因為沒有進行同步,而且我們沒有必要,因為大部分情況根本沒有多線程環境)。
2、 什麼叫原子的(原子操作)?
Java原子操作是指:不會被打斷地的操作。(就是做到互斥 和可見性?!)
那難道原子操作就可以真的達到線程安全同步效果了嗎?實際上有一些原子操作不一定是線程安全的。
那麼,原子操作在什麼情況下不是線程安全的呢?也許是這個原因導致的:java線程允許線程在自己的內存區保存變數的副本。允許線程使用本地的私有拷貝進
行工作而非每次都使用主存的值是為了提高性能(本人愚見:雖然原子操作是線程安全的,可各線程在得到變數(讀操作)後,就是各自玩
弄自己的副本了,更新操作(寫操作)因未寫入主存中,導致其它線程不可見)。
那該如何解決呢?因此需要通過java同步機制。
在java中,32位或者更少位數的賦值是原子的。在一個32位的硬體平台上,除了double和long型的其它原始類型通常都
是使用32位進行表示,而double和long通常使用64位表示。另外,對象引用使用本機指針實現,通常也是32位的。對這些32位的類型的操作是原
子的。
這些原始類型通常使用32位或者64位表示,這又引入了另一個小小的神話:原始類型的大小是由語言保證的。這是不對的。java語言保證的是原始類型的表
數范圍而非JVM中的存儲大小。因此,int型總是有相同的表數范圍。在一個JVM上可能使用32位實現,而在另一個JVM上可能是64位的。在此再次強
調:在所有平台上被保證的是表數范圍,32位以及更小的值的操作是原子的。
3、 不要搞混了:同步、非同步
舉個例子:普通B/S模式(同步)AJAX技術(非同步)
同步:提交請求->等待伺服器處理->處理完返回 這個期間客戶端瀏覽器不能幹任何事
非同步:請求通過事件觸發->伺服器處理(這是瀏覽器仍然可以作其他事情)->處理完畢
可見,彼「同步」非此「同步」——我們說的java中的那個共享數據同步(synchronized)
一個同步的對象是指行為(動作),一個是同步的對象是指物質(共享數據)。
4、 Java同步機制有4種實現方式:(部分引用網上資源)
① ThreadLocal ② synchronized( ) ③ wait() 與 notify() ④ volatile
目的:都是為了解決多線程中的對同一變數的訪問沖突
ThreadLocal
ThreadLocal 保證不同線程擁有不同實例,相同線程一定擁有相同的實例,即為每一個使用該變數的線程提供一個該變數值的副本,每一個線程都可以獨立改變自己的副本,而不是與其它線程的副本沖突。
優勢:提供了線程安全的共享對象
與其它同步機制的區別:同步機制是為了同步多個線程對相同資源的並發訪問,是為了多個線程之間進行通信;而 ThreadLocal 是隔離多個線程的數據共享,從根本上就不在多個線程之間共享資源,這樣當然不需要多個線程進行同步了。
volatile
volatile 修飾的成員變數在每次被線程訪問時,都強迫從共享內存中重讀該成員變數的值。而且,當成員變數發生變化時,強迫線程將變化值回寫到共享內存。
優勢:這樣在任何時刻,兩個不同的線程總是看到某個成員變數的同一個值。
緣由:Java
語言規范中指出,為了獲得最佳速度,允許線程保存共享成員變數的私有拷貝,而且只當線程進入或者離開同步代碼塊時才與共享成員變數的原
始值對比。這樣當多個線程同時與某個對象交互時,就必須要注意到要讓線程及時的得到共享成員變數的變化。而 volatile
關鍵字就是提示 VM :對於這個成員變數不能保存它的私有拷貝,而應直接與共享成員變數交互。
使用技巧:在兩個或者更多的線程訪問的成員變數上使用 volatile 。當要訪問的變數已在 synchronized 代碼塊中,或者為常量時,不必使用。
線程為了提高效率,將某成員變數(如A)拷貝了一份(如B),線程中對A的訪問其實訪問的是B。只在某些動作時才進行A和B的同步,因此存在A和B不一致
的情況。volatile就是用來避免這種情況的。
volatile告訴jvm,它所修飾的變數不保留拷貝,直接訪問主內存中的(讀操作多時使用較好;線程間需要通信,本條做不到)
Volatile 變數具有 synchronized 的可見性特性,但是不具備原子特性。這就是說線程能夠自動發現 volatile
變數的最新值。Volatile
變數可用於提供線程安全,但是只能應用於非常有限的一組用例:多個變數之間或者某個變數的當前值與修改後值
之間沒有約束。
您只能在有限的一些情形下使用 volatile 變數替代鎖。要使 volatile 變數提供理想的線程安全,必須同時滿足下面兩個條件:
對變數的寫操作不依賴於當前值;該變數沒有包含在具有其他變數的不變式中。
sleep() vs wait()
sleep是線程類(Thread)的方法,導致此線程暫停執行指定時間,把執行機會給其他線程,但是監控狀態依然保持,到時後會自動恢復。調用sleep不會釋放對象鎖。
wait是Object類的方法,對此對象調用wait方法導致本線程放棄對象鎖,進入等待此對象的等待鎖定池,只有針對此對象發出notify方法(或notifyAll)後本線程才進入對象鎖定池准備獲得對象鎖進入運行狀態。
(如果變數被聲明為volatile,在每次訪問時都會和主存一致;如果變數在同步方法或者同步塊中被訪問,當在方法或者塊的入口處獲得鎖以及方法或者塊退出時釋放鎖時變數被同步。)
『肆』 某一線程阻塞時,其它線程也阻塞嗎
假設你有一個主線程,線程名為:Thread_A,然後通過Thread_A創建了線程Thread_B、Thread_C,並將線程Thread_B、Thread_C作為局部變數的方式存儲在Thread_A中,並調用Thread_B、Thread_C的start()方法開始執行Thread_B、Thread_C
『伍』 當一個處於阻塞狀態的線程解除阻塞後,他將回到哪個狀態 A運行中狀態 B結束狀態 C新建狀態 D可運行狀態
如果你有兩個線程同時等在一個synchronized方法上,那麼當其中一個結束執行的時候,另一個將自動得到機會執行
如果你手動讓一個線程wait在一個對象obj上,那麼你需要在合適的時候調用obj.notifyAll()來喚醒那些等待的線程,它們中的一個將會得到執行。
『陸』 JAVA在線程A中啟動線程B(B同時是A的子類),線程B無法進入執行是怎麼回事
線程A一直擁有CPU執行權-----這個不好判斷;
線程B是否在某個特定的條件下執行。。。就是線程A裡面有判斷語句。。。如果成立了才啟動B線程?如果是的話。。。檢查一下判斷參數的傳入值。。。看是否會為執行的條件。
『柒』 java中如何使一個線程進入阻塞態
假設你有一個主線程,線程名為:Thread_A,然後通過Thread_A創建了線程Thread_B、Thread_C,並將線程Thread_B、Thread_C作為局部變數的方式存儲在Thread_A中,並調用Thread_B、Thread_C的start()方法開始執行Thread_B、Thread_C,當Thread_A執行到你要停止的地方就分別調用Thread_B、Thread_C的Wait()方法,使Thread_B、Thread_C暫停,然後線程Thread_A繼續執行,直到Thread_A中調用Thread_B、Thread_C的notify()方法使得Thread_B、Thread_C繼續執行,大體上就是這樣!
『捌』 如何讓線程阻塞,如何喚醒線程,說說不同的喚醒線程方法的區別
sleep() 方法:
sleep(…毫秒),指定以毫秒為單位的時間,使線程在該時間內進入線程阻塞狀態,期間得不到cpu的時間片,等到時間過去了,線程重新進入可執行狀態。(暫停線程,不會釋放鎖)suspend() 和 resume() 方法:。
掛起和喚醒線程,suspend()使線程進入阻塞狀態,只有對應的resume()被調用的時候,線程才會進入可執行狀態。(不建議用,容易發生死鎖)yield() 方法:
會使的線程放棄當前分得的cpu時間片,但此時線程任然處於可執行狀態,隨時可以再次分得cpu時間片。yield()方法只能使同優先順序的線程有執行的機會。調用 yield() 的效果等價於調度程序認為該線程已執行了足夠的時間從而轉到另一個線程。(暫停當前正在執行的線程,並執行其他線程,且讓出的時間不可知)wait() 和 notify() 方法:
兩個方法搭配使用,wait()使線程進入阻塞狀態,調用notify()時,線程進入可執行狀態。wait()內可加或不加參數,加參數時是以毫秒為單位,當到了指定時間或調用notify()方法時,進入可執行狀態。(屬於Object類,而不屬於Thread類,wait( )會先釋放鎖住的對象,然後再執行等待的動作。由於wait( )所等待的對象必須先鎖住,因此,它只能用在同步化程序段或者同步化方法內,否則,會拋出異常IllegalMonitorStateException.)join()方法
也叫線程加入。是當前線程A調用另一個線程B的join()方法,當前線程轉A入阻塞狀態,直到線程B運行結束,線程A才由阻塞狀態轉為可執行狀態。
『玖』 java中如何在一個線程中阻塞其他線程
假設你有一個主線程,線程名為:Thread_A,然後通過Thread_A創建了線程Thread_B、Thread_C,並將線程Thread_B、Thread_C作為局部變數的方式存儲在Thread_A中,並調用Thread_B、Thread_C的start()方法開始執行Thread_B、Thread_C,當Thread_A執行到你要停止的地方就分別調用Thread_B、Thread_C的Wait()方法,使Thread_B、Thread_C暫停,然後線程Thread_A繼續執行,直到Thread_A中調用Thread_B、Thread_C的notify()方法使得Thread_B、Thread_C繼續執行,大體上就是這樣!