有兩種實現(xiàn)方法,分別是繼承Thread類與實現(xiàn)Runnable
成都創(chuàng)新互聯(lián)自2013年創(chuàng)立以來,先為雷州等服務建站,雷州等地企業(yè),進行企業(yè)商務咨詢服務。為雷州企業(yè)網(wǎng)站制作PC+手機+微官網(wǎng)三網(wǎng)同步一站式服務解決您的所有建站問題。
接口
用synchronized關鍵字修飾同步方法
反對使用stop(),是因為它不安全。它會解除由線程獲取的所有鎖定,而且如果對象處于一種不連貫狀態(tài),那么
其他線程能在那種狀態(tài)下檢查和修改它們。結果很難檢查出真正的問題所在。suspend()方法容易發(fā)生死鎖。調用
suspend()的時候,目標線程會停下來,但卻仍然持有在這之前獲得的鎖定。此時,其他任何線程都不能訪問鎖定
的資源,除非被"掛起"的線程恢復運行。對任何線程來說,如果它們想恢復目標線程,同時又試圖使用任何一個
鎖定的資源,就會造成死鎖。所以不應該使用suspend(),而應在自己的Thread類中置入一個標志,指出線程應該
活動還是掛起。若標志指出線程應該掛起,便用wait()命其進入等待狀態(tài)。若標志指出線程應當恢復,則用一個
notify()重新啟動線程。
主線程保持著A對象的鎖意思就是主線程正在處理A對象,其他線程不能處理,要等待主線程結束之后其他線程才能處理A對象。
同理副線程正在處理B對象,A不能處理,所以主線程結束不了,一直在等待。
兩個線程都運行不下去了就叫做死鎖,程序崩潰。
加鎖的意思就是某線程正在處理某對象,其他線程不能處理。
手打不容易,明白不明白都給分吧- -、
在編程中使用ThreadMXBean類來檢測死鎖
Java 5引入了ThreadMXBean接口,它提供了多種監(jiān)視線程的方法。我建議您了解所有這些方法,因為當您沒使用外部工具時,它們會為您提供很多有用的操作以便您監(jiān)測程序性能。這里,我們感興趣的方法是findMonitorDeadlockedThreads,如過您使用的是Java 6,對應的方法是findDeadlockedThreads。二者的區(qū)別的是,findDeadlockedThreads還可以檢測到owner locks(java.util.concurrent)引起的死鎖,而findMonitorDeadlockedThreads只能檢測monitor locks(例如,同步塊)。由于保留老版本的方法只是出于兼容性的考慮,所以我將使用新版本的方法。在這里,編程的思想是把對死鎖的周期性檢測封裝到一個可重用組件里,之后我們只需啟動它、隨它去。
一種實現(xiàn)調度的方法是通過執(zhí)行器框架,即一組良好抽象并易于使用的多線程類。
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
this.scheduler.scheduleAtFixedRate(deadlockCheck, period, period, unit);
就是那么簡單,在我們通過選擇周期和時間單位而設置了一個特定時間后,就得到了一個周期性調用的線程。接著,我們想使功用得以拓展從而允許用戶提供在程序檢測到死鎖時所觸發(fā)的行為。最后,我們需要一個方法來接收用于描述死鎖中所有線程的一系列對象。
void handleDeadlock(final ThreadInfo[] deadlockedThreads);
死鎖
死鎖是這樣一種情形:多個線程同時被阻塞,它們中的一個或者全部都在等待某個資源被釋放。由于線程被無限期地阻塞,因此程序不可能正常終止。
導致死鎖的根源在于不適當?shù)剡\用“synchronized”關鍵詞來管理線程對特定對象的訪問?!皊ynchronized”關鍵詞的作用是,確保在某個時刻只有一個線程被允許執(zhí)行特定的代碼塊,因此,被允許執(zhí)行的線程首先必須擁有對變量或對象的排他性的訪問權。當線程訪問對象時,線程會給對象加鎖,而這個鎖導致其它也想訪問同一對象的線程被阻塞,直至第一個線程釋放它加在對象上的鎖。
由于這個原因,在使用“synchronized”關鍵詞時,很容易出現(xiàn)兩個線程互相等待對方做出某個動作的情形。代碼一是一個導致死鎖的簡單例子。
//代碼一
class Deadlocker {
int field_1;
private Object lock_1 = new int[1];
int field_2;
private Object lock_2 = new int[1];
public void method1(int value) {
“synchronized” (lock_1) {
“synchronized” (lock_2) {
field_1 = 0; field_2 = 0;
}
}
}
public void method2(int value) {
“synchronized” (lock_2) {
“synchronized” (lock_1) {
field_1 = 0; field_2 = 0;
}
}
}
}
參考代碼一,考慮下面的過程:
◆ 一個線程(ThreadA)調用method1()。
◆ ThreadA在lock_1上同步,但允許被搶先執(zhí)行。
◆ 另一個線程(ThreadB)開始執(zhí)行。
◆ ThreadB調用method2()。
◆ ThreadB獲得lock_2,繼續(xù)執(zhí)行,企圖獲得lock_1。但ThreadB不能獲得lock_1,因為ThreadA占有l(wèi)ock_1。
◆ 現(xiàn)在,ThreadB阻塞,因為它在等待ThreadA釋放lock_1。
◆ 現(xiàn)在輪到ThreadA繼續(xù)執(zhí)行。ThreadA試圖獲得lock_2,但不能成功,因為lock_2已經(jīng)被ThreadB占有了。
◆ ThreadA和ThreadB都被阻塞,程序死鎖。
當然,大多數(shù)的死鎖不會這么顯而易見,需要仔細分析代碼才能看出,對于規(guī)模較大的多線程程序來說尤其如此。好的線程分析工具,例如JProbe Threadalyzer能夠分析死鎖并指出產(chǎn)生問題的代碼位置。
隱性死鎖
隱性死鎖由于不規(guī)范的編程方式引起,但不一定每次測試運行時都會出現(xiàn)程序死鎖的情形。由于這個原因,一些隱性死鎖可能要到應用正式發(fā)布之后才會被發(fā)現(xiàn),因此它的危害性比普通死鎖更大。下面介紹兩種導致隱性死鎖的情況:加鎖次序和占有并等待。
加鎖次序
當多個并發(fā)的線程分別試圖同時占有兩個鎖時,會出現(xiàn)加鎖次序沖突的情形。如果一個線程占有了另一個線程必需的鎖,就有可能出現(xiàn)死鎖??紤]下面的情形,ThreadA和ThreadB兩個線程分別需要同時擁有l(wèi)ock_1、lock_2兩個鎖,加鎖過程可能如下:
◆ ThreadA獲得lock_1;
◆ ThreadA被搶占,VM調度程序轉到ThreadB;
◆ ThreadB獲得lock_2;
◆ ThreadB被搶占,VM調度程序轉到ThreadA;
◆ ThreadA試圖獲得lock_2,但lock_2被ThreadB占有,所以ThreadA阻塞;
◆ 調度程序轉到ThreadB;
◆ ThreadB試圖獲得lock_1,但lock_1被ThreadA占有,所以ThreadB阻塞;
◆ ThreadA和ThreadB死鎖。
必須指出的是,在代碼絲毫不做變動的情況下,有些時候上述死鎖過程不會出現(xiàn),VM調度程序可能讓其中一個線程同時獲得lock_1和lock_2兩個鎖,即線程獲取兩個鎖的過程沒有被中斷。在這種情形下,常規(guī)的死鎖檢測很難確定錯誤所在。
占有并等待
如果一個線程獲得了一個鎖之后還要等待來自另一個線程的通知,可能出現(xiàn)另一種隱性死鎖,考慮代碼二。
//代碼二
public class queue {
static java.lang.Object queueLock_;
Producer producer_;
Consumer consumer_;
public class Producer {
void produce() {
while (!done) {
“synchronized” (queueLock_) {
produceItemAndAddItToQueue();
“synchronized” (consumer_) {
consumer_.notify();
}
}
}
}
public class Consumer {
consume() {
while (!done) {
“synchronized” (queueLock_) {
“synchronized” (consumer_) {
consumer_.wait();
}
removeItemFromQueueAndProcessIt();
}
}
}
}
}
}
在代碼二中,Producer向隊列加入一項新的內容后通知Consumer,以便它處理新的內容。問題在于,Consumer可能保持加在隊列上的鎖,阻止Producer訪問隊列,甚至在Consumer等待Producer的通知時也會繼續(xù)保持鎖。這樣,由于Producer不能向隊列添加新的內容,而Consumer卻在等待Producer加入新內容的通知,結果就導致了死鎖。
在等待時占有的鎖是一種隱性的死鎖,這是因為事情可能按照比較理想的情況發(fā)展—Producer線程不需要被Consumer占據(jù)的鎖。盡管如此,除非有絕對可靠的理由肯定Producer線程永遠不需要該鎖,否則這種編程方式仍是不安全的。有時“占有并等待”還可能引發(fā)一連串的線程等待,例如,線程A占有線程B需要的鎖并等待,而線程B又占有線程C需要的鎖并等待等。
要改正代碼二的錯誤,只需修改Consumer類,把wait()移出“synchronized”()即可。
在基于Java Swing進行圖形界面開發(fā)的時候 經(jīng)常遇到的就是Swing多線程問題 我們可以想想一下 如果需要在一個圖形界面上顯示很多數(shù)據(jù) 這些數(shù)據(jù)是經(jīng)過長時間 復雜的查詢和運算得到的 如果在圖形界面的同一個線程中進行查詢和運算工作則會導致一段時間界面處于死機狀態(tài) 這會給用戶帶來不良的互動感受 為了解決這個問題 一般會單獨啟動一個線程進行運算和查詢工作 并隨時更新圖形界面 這時候 另一個問題就出現(xiàn)了 可能不僅沒有解決原來偶爾死機問題 還可能導致程序徹底死掉 幸運的是在JDK中暗藏了一個中斷程序的快捷鍵 就是CTRL+BREAK 這個快捷鍵Sun并沒有在文檔中公布 如果在命令行模式下啟動Java程序 然后按CTRL+BREAK鍵 會得到堆棧的跟蹤信息 從這些跟蹤信息中就可以知道具體引發(fā)死機的位置了
當一個程序產(chǎn)生死鎖的時候 你一定會希望盡快找到原因并且解決它 這時候 你一般的精力會用在查找引發(fā)死鎖的位置 另一半的精力會用于對堆棧進行跟蹤一確定引發(fā)死鎖的原因 但是在Java Swing程序中 你的所有努力可能都是沒有價值的 這是因為Java對Swing的多線程編程有一個特殊要求 就是在Swing里 只能在與Swing相同的線程里對GUI元件進行修改
也就是說 如果你要執(zhí)行類似于jLabel setText( blabla )代碼 必須在Swing線程中 而不允許在其他線程當中 如果必須在其他線程中修改元件 可以使用類似一下方式解決
SwingUtilities invokeLater(new Runnable() {
public void run() {
jLabel setText( blabla );
}
}
invokeLater方法雖然表面有時間延遲執(zhí)行含義 但是實際上幾乎沒有任何影響 可能在幾毫秒之內就會被執(zhí)行 另外還有一個invokeAndWait方法 除非特殊需要 否則幾乎是不用的
在不使用invokeLater的情況下 導致刷新問題是可以理解的 但是導致死鎖就優(yōu)點令人匪夷所思了 幸運的是 不是任何時候都需要調用改方法 這是因為大多數(shù)情況下 我們都是在與Swing同一個線程里進行界面更新 例如監(jiān)聽按鈕點擊事件的ActionListener actionPerformed方法就是運行在與Swing相同的線程中的 但是如果在回調類中引用了另一個類 并且是不屬于AWT/Swing的 那么結果就很難確定了 所以說使用invokeLater應該是最安全的
需要注意的是 在invokeLater做的任何事情 都會導致Swing線程窗口繪制工作暫停下來 等候invokeLater工作結束 所以不要在invokeLater進行耗時操作 盡量只執(zhí)行那些界面繪制相關的工作 可以通過代碼重構 將那些與界面更新相關的代碼集中起來統(tǒng)一處理
lishixinzhi/Article/program/Java/gj/201311/27498
每個使用關系型數(shù)據(jù)庫的程序都可能遇到數(shù)據(jù)死鎖或不可用的情況,而這些情況需要在代碼中編程來解決;本文主要介紹與數(shù)據(jù)庫事務死鎖等情況相關的重試邏輯概念,此外,還會探討如何避免死鎖等問題,文章以DB2(版本9)與為例進行講解。
什么是數(shù)據(jù)庫鎖定與死鎖鎖定(Locking)發(fā)生在當一個事務獲得對某一資源的“鎖”時,這時,其他的事務就不能更改這個資源了,這種機制的存在是為了保證數(shù)據(jù)一致性;在設計與數(shù)據(jù)庫交互的程序時,必須處理鎖與資源不可用的情況。
鎖定是個比較復雜的概念,仔細說起來可能又需要一大篇,所以在本文中,只把鎖定看作是一個臨時事件,這意味著如果一個資源被鎖定,它總會在以后某個時間被釋放。
而死鎖發(fā)生在當多個進程訪問同一數(shù)據(jù)庫時,其中每個進程擁有的鎖都是其他進程所需的,由此造成每個進程都無法繼續(xù)下去。
如何避免鎖我們可利用事務型數(shù)據(jù)庫中的隔離級別機制來避免鎖的創(chuàng)建,正確地使用隔離級別可使程序處理更多的并發(fā)事件(如允許多個用戶訪問數(shù)據(jù)),還能預防像丟失修改(LostUpdate)、讀“臟”數(shù)據(jù)(DirtyRead)、不可重復讀(NonrepeatableRead)及“虛”(Phantom)等問題。
隔離級別問題現(xiàn)象丟失修改讀“臟”數(shù)據(jù)不可重復讀“虛”可重復讀取NoNoNoNo讀取穩(wěn)定性NoNoNoYes光標穩(wěn)定性NoNoYesYes未提交的讀NoYesYesYes表1:DB2的隔離級別與其對應的問題現(xiàn)象在只讀模式中,就可以防止鎖定發(fā)生,而不用那些未提交只讀隔離級別的含糊語句。
浙江電腦培訓發(fā)現(xiàn)一條SQL語句當使用了下列命令之一時,就應該考慮只讀模式了