java同步多線程
⑴ java多線程調用同步方法時,休眠後代碼為什麼執行不到
1、型正java多線程調用同步方法時主程序或者後台服務Service程猜租答序兩者必須有一個正在運行著,定時任務才會執行。
2、自動穗慧執行的任務可以設置開啟或關閉定時,檢查任務的這一開關處於開啟狀態。
3、源目錄在定時的時間點如果沒有變化,任務執行後不會留下日誌,通過查看任務的最後執行時間確定任務到底執行了沒有。
4、執行失敗和沒有執行不是同一回事,如果是執行失敗則要分析失敗原因。這是java多線程調用同步方法時,休眠後代碼執行不到的原因。
⑵ Java多線程初學者指南(10):使用Synchronized關鍵字同步類方法
要想解決 臟數據 的問題 最簡單的方法就是使用synchronized關鍵字來使run方法同步 代碼如下
publicsynchronizedvoidrun(){}
從上面的代碼可以看出 只要在void和public之間加上synchronized關鍵字 就可以使run方法同步 也就是說 對於同一個Java類的對象實例 run方法同時只能被一個線程調用 並當前的run執行完後 才能被其他的線程調用 即使當前線程執行到了run方法中的yield方法 也只是暫停了一下 由於其他線程無法執行run方法 因此 最終還是會由當前的線程來繼續執行 先看看下面的代碼
sychronized關鍵字只和一個對象實例綁定
classTest{publicsynchronizedvoidmethod(){}納察}{privateTesttest;publicvoidrun() { thod(); } publicSync(Testtest) { this test=test; } publicstaticvoidmain(String[]args)throwsException { Testtest =newTest(); Testtest =newTest(); Syncsync =newSync(test ); Syncsync =newSync(test ); newThread(sync ) start(); newThread(sync ) start(); } }
在Test類中的method方法是同步的 但上面的代碼建立了兩個Test類的實例 因此 test 和test 的method方法是分別執行的 要想讓method同步 必須在建立仿茄升Sync類的實例時向它的構造方法中傳入同一個Test類的實例 如下面的代碼所示
Syncsync =newSync(test );
不僅可以使用synchronized來同步非靜態方法 也可以使用synchronized來同步靜態方法 如可以按如下方式來定義method方法
classTest{ (){}}
建立Test類的對象實例如下
Testtest=newTest();
對於靜態方法來說 只要加上了synchronized關鍵字 這個方法就是同步的 無論是使用thod() 還是使用thod()來調用method方法 method都是同步的 並不存在非靜態方法的多個實例的問題
在 種設計模式中的單件(Singleton)模式如果按傳統的方法設計 也是線程不安全的 下面的代碼是一個線程不安全的單件模式
packagetest;//線程安全的Singleton模式classSingleton{privatestaticSingletonsample;privateSingleton(){備老}(){if(sample==null){Thread yield();//為了放大Singleton模式的線程不安全性sample=newSingleton();}returnsample;}}{publicvoidrun(){Singletonsingleton=Singleton getInstance();System out println(singleton hashCode());}publicstaticvoidmain(String[]args){Threadthreads[]=newThread[ ];for(inti= ;i<threads length;i++)threads[i]=newMyThread();for(inti= ;i<threads length;i++)threads[i] start();}}
在上面的代碼調用yield方法是為了使單件模式的線程不安全性表現出來 如果將這行去掉 上面的實現仍然是線程不安全的 只是出現的可能性小得多
程序的運行結果如下
上面的運行結果可能在不同的運行環境上有所有同 但一般這五行輸出不會完全相同 從這個輸出結果可以看出 通過getInstance方法得到的對象實例是五個 而不是我們期望的一個 這是因為當一個線程執行了Thread yield()後 就將CPU資源交給了另外一個線程 由於在線程之間切換時並未執行到創建Singleton對象實例的語句 因此 這幾個線程都通過了if判斷 所以 就會產生了建立五個對象實例的情況(可能創建的是四個或三個對象實例 這取決於有多少個線程在創建Singleton對象之前通過了if判斷 每次運行時可能結果會不一樣)
要想使上面的單件模式變成線程安全的 只要為getInstance加上synchronized關鍵字即可 代碼如下
(){}
當然 還有更簡單的方法 就是在定義Singleton變數時就建立Singleton對象 代碼如下
=newSingleton();
然後在getInstance方法中直接將sample返回即可 這種方式雖然簡單 但不知在getInstance方法中創建Singleton對象靈活 讀者可以根據具體的需求選擇使用不同的方法來實現單件模式
在使用synchronized關鍵字時有以下四點需要注意
synchronized關鍵字不能繼承
雖然可以使用synchronized來定義方法 但synchronized並不屬於方法定義的一部分 因此 synchronized關鍵字不能被繼承 如果在父類中的某個方法使用了synchronized關鍵字 而在子類中覆蓋了這個方法 在子類中的這個方法默認情況下並不是同步的 而必須顯式地在子類的這個方法中加上synchronized關鍵字才可以 當然 還可以在子類方法中調用父類中相應的方法 這樣雖然子類中的方法不是同步的 但子類調用了父類的同步方法 因此 子類的方法也就相當於同步了 這兩種方式的例子代碼如下
在子類方法中加上synchronized關鍵字
classParent{ publicsynchronizedvoidmethod(){}}classChildextendsParent{ publicsynchronizedvoidmethod(){}}
在子類方法中調用父類的同步方法
classParent{ publicsynchronizedvoidmethod(){}}classChildextendsParent{publicvoidmethod(){thod();}}
在定義介面方法時不能使用synchronized關鍵字
構造方法不能使用synchronized關鍵字 但可以使用下節要討論的synchronized塊來進行同步
synchronized可以自由放置
在前面的例子中使用都是將synchronized關鍵字放在方法的返回類型前面 但這並不是synchronized可放置唯一位置 在非靜態方法中 synchronized還可以放在方法定義的最前面 在靜態方法中 synchronized可以放在static的前面 代碼如下
publicsynchronizedvoidmethod();synchronizedpublicvoidmethod();();();();
但要注意 synchronized不能放在方法返回類型的後面 如下面的代碼是錯誤的
publicvoidsynchronizedmethod();();
synchronized關鍵字只能用來同步方法 不能用來同步類變數 如下面的代碼也是錯誤的
publicsynchronizedintn= ;publicstaticsynchronizedintn= ;
雖然使用synchronized關鍵字同步方法是最安全的同步方式 但大量使用synchronized關鍵字會造成不必要的資源消耗以及性能損失 雖然從表面上看synchronized鎖定的是一個方法 但實際上synchronized鎖定的是一個類 也就是說 如果在非靜態方法method 和method 定義時都使用了synchronized 在method 未執行完之前 method 是不能執行的 靜態方法和非靜態方法的情況類似 但靜態和非靜態方法不會互相影響 看看如下的代碼
packagetest;publicclassMyThread extendsThread{publicStringmethodName;publicstaticvoidmethod(Strings){System out println(s);while(true);}publicsynchronizedvoidmethod (){method( 非靜態的method 方法 );}publicsynchronizedvoidmethod (){method( 非靜態的method 方法 );} (){method( 靜態的method 方法 );} (){method( 靜態的method 方法 );}publicvoidrun(){try{getClass() getMethod(methodName) invoke(this);}catch(Exceptione){}}publicstaticvoidmain(String[]args)throwsException{MyThread myThread =newMyThread ();for(inti= ;i<= ;i++){thodName= method +String valueOf(i);newThread(myThread ) start();sleep( );}}}
運行結果如下
非靜態的method 方法靜態的method 方法
lishixin/Article/program/Java/gj/201311/27526
⑶ java多線程有幾種實現方法,都是什麼同步有幾種實現方法,都是什麼
java中多線程的實現方法有兩種:1.直接繼承thread類;2.實現runnable介面;同步的實現方法有五種:1.同步方法;2.同步代碼塊;3.使用特殊域變數(volatile)實現線程同步;4.使用重入鎖實現線程同步;5.使用局部變數實現線程同步
。
其中多線程實現過程中需注意重寫或者覆蓋run()方法,而對於同步的實現方法中使用較常使用的是利用synchronized編寫同步方法和代碼塊。
⑷ java多線程有幾種實現方法線程之間如何同步
一、為什麼要線程同步
因為當我們有多個線程要同時訪問一個變數或對象時,如果這些線程中既有讀又有寫操作時,就會導致變數值或對象的狀態出現混亂,從而導致程序異常。舉個例子,如果一個銀行賬戶同時被兩個線程操作,一個取100塊,一個存錢100塊。假設賬戶原本有0塊,如果取錢線程和存錢線程同時發生,會出現什麼結果呢?取錢不成功,賬戶余額是100.取錢成功了,賬戶余額是0.那到底是哪個呢?很難說清楚。因此多線程同步就是要解決這個問題。
二、不同步時的代碼
Bank.Java
packagethreadTest;
/**
*@authorww
*
*/
publicclassBank{
privateintcount=0;//賬戶余額
//存錢
publicvoidaddMoney(intmoney){
count+=money;
System.out.println(System.currentTimeMillis()+"存進:"+money);
}
//取錢
publicvoidsubMoney(intmoney){
if(count-money<0){
System.out.println("余額不足");
return;
}
count-=money;
System.out.println(+System.currentTimeMillis()+"取出:"+money);
}
//查詢
publicvoidlookMoney(){
System.out.println("賬戶余額:"+count);
}
}
SyncThreadTest.java
packagethreadTest;
publicclassSyncThreadTest{
publicstaticvoidmain(Stringargs[]){
finalBankbank=newBank();
Threadtadd=newThread(newRunnable(){
@Override
publicvoidrun(){
//TODOAuto-generatedmethodstub
while(true){
try{
Thread.sleep(1000);
}catch(InterruptedExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}
bank.addMoney(100);
bank.lookMoney();
System.out.println(" ");
}
}
});
Threadtsub=newThread(newRunnable(){
@Override
publicvoidrun(){
//TODOAuto-generatedmethodstub
while(true){
bank.subMoney(100);
bank.lookMoney();
System.out.println(" ");
try{
Thread.sleep(1000);
}catch(InterruptedExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}
}
}
});
tsub.start();
tadd.start();
}
}
余額不足
賬戶余額:0
余額不足
賬戶余額:100
1441790503354存進:100
賬戶余額:100
1441790504354存進:100
賬戶余額:100
1441790504354取出:100
賬戶余額:100
1441790505355存進:100
賬戶余額:100
1441790505355取出:100
賬戶余額:100
三、使用同步時的代碼
(1)同步方法:
即有synchronized關鍵字修飾的方法。由於java的每個對象都有一個內置鎖,當用此關鍵字修飾方法時,內置鎖會保護整個方法。在調用該方法前,需要獲得內置鎖,否則就處於阻塞狀態。
修改後的Bank.java
packagethreadTest;
/**
*@authorww
*
*/
publicclassBank{
privateintcount=0;//賬戶余額
//存錢
(intmoney){
count+=money;
System.out.println(System.currentTimeMillis()+"存進:"+money);
}
//取錢
(intmoney){
if(count-money<0){
System.out.println("余額不足");
return;
}
count-=money;
System.out.println(+System.currentTimeMillis()+"取出:"+money);
}
//查詢
publicvoidlookMoney(){
System.out.println("賬戶余額:"+count);
}
}
再看看運行結果:
余額不足
賬戶余額:0
余額不足
賬戶余額:0
1441790837380存進:100
賬戶余額:100
1441790838380取出:100
賬戶余額:0
1441790838380存進:100
賬戶余額:100
1441790839381取出:100
賬戶余額:0
瞬間感覺可以理解了吧。
註: synchronized關鍵字也可以修飾靜態方法,此時如果調用該靜態方法,將會鎖住整個類
(2)同步代碼塊
即有synchronized關鍵字修飾的語句塊。被該關鍵字修飾的語句塊會自動被加上內置鎖,從而實現同步
Bank.java代碼如下:
packagethreadTest;
/**
*@authorww
*
*/
publicclassBank{
privateintcount=0;//賬戶余額
//存錢
publicvoidaddMoney(intmoney){
synchronized(this){
count+=money;
}
System.out.println(System.currentTimeMillis()+"存進:"+money);
}
//取錢
publicvoidsubMoney(intmoney){
synchronized(this){
if(count-money<0){
System.out.println("余額不足");
return;
}
count-=money;
}
System.out.println(+System.currentTimeMillis()+"取出:"+money);
}
//查詢
publicvoidlookMoney(){
System.out.println("賬戶余額:"+count);
}
}
運行結果如下:
余額不足
賬戶余額:0
1441791806699存進:100
賬戶余額:100
1441791806700取出:100
賬戶余額:0
1441791807699存進:100
賬戶余額:100
效果和方法一差不多。
註:同步是一種高開銷的操作,因此應該盡量減少同步的內容。通常沒有必要同步整個方法,使用synchronized代碼塊同步關鍵代碼即可。
(3)使用特殊域變數(volatile)實現線程同步
a.volatile關鍵字為域變數的訪問提供了一種免鎖機制
b.使用volatile修飾域相當於告訴虛擬機該域可能會被其他線程更新
c.因此每次使用該域就要重新計算,而不是使用寄存器中的值
d.volatile不會提供任何原子操作,它也不能用來修飾final類型的變數
Bank.java代碼如下:
packagethreadTest;
/**
*@authorww
*
*/
publicclassBank{
privatevolatileintcount=0;//賬戶余額
//存錢
publicvoidaddMoney(intmoney){
count+=money;
System.out.println(System.currentTimeMillis()+"存進:"+money);
}
//取錢
publicvoidsubMoney(intmoney){
if(count-money<0){
System.out.println("余額不足");
return;
}
count-=money;
System.out.println(+System.currentTimeMillis()+"取出:"+money);
}
//查詢
publicvoidlookMoney(){
System.out.println("賬戶余額:"+count);
}
}
運行效果怎樣呢?
余額不足
賬戶余額:0
余額不足
賬戶余額:100
1441792010959存進:100
賬戶余額:100
1441792011960取出:100
賬戶余額:0
1441792011961存進:100
賬戶余額:100
是不是又看不懂了,又亂了。這是為什麼呢?就是因為volatile不能保證原子操作導致的,因此volatile不能代替synchronized。此外volatile會組織編譯器對代碼優化,因此能不使用它就不適用它吧。它的原理是每次要線程要訪問volatile修飾的變數時都是從內存中讀取,而不是存緩存當中讀取,因此每個線程訪問到的變數值都是一樣的。這樣就保證了同步。
(4)使用重入鎖實現線程同步
在JavaSE5.0中新增了一個java.util.concurrent包來支持同步。ReentrantLock類是可重入、互斥、實現了Lock介面的鎖,它與使用synchronized方法和快具有相同的基本行為和語義,並且擴展了其能力。
ReenreantLock類的常用方法有:
ReentrantLock() : 創建一個ReentrantLock實例
lock() : 獲得鎖
unlock() : 釋放鎖
註:ReentrantLock()還有一個可以創建公平鎖的構造方法,但由於能大幅度降低程序運行效率,不推薦使用
Bank.java代碼修改如下:
packagethreadTest;
importjava.util.concurrent.locks.Lock;
importjava.util.concurrent.locks.ReentrantLock;
/**
*@authorww
*
*/
publicclassBank{
privateintcount=0;//賬戶余額
//需要聲明這個鎖
privateLocklock=newReentrantLock();
//存錢
publicvoidaddMoney(intmoney){
lock.lock();//上鎖
try{
count+=money;
System.out.println(System.currentTimeMillis()+"存進:"+money);
}finally{
lock.unlock();//解鎖
}
}
//取錢
publicvoidsubMoney(intmoney){
lock.lock();
try{
if(count-money<0){
System.out.println("余額不足");
return;
}
count-=money;
System.out.println(+System.currentTimeMillis()+"取出:"+money);
}finally{
lock.unlock();
}
}
//查詢
publicvoidlookMoney(){
System.out.println("賬戶余額:"+count);
}
}
運行效果怎麼樣呢?
余額不足
賬戶余額:0
余額不足
賬戶余額:0
1441792891934存進:100
賬戶余額:100
1441792892935存進:100
賬戶余額:200
1441792892954取出:100
賬戶余額:100
效果和前兩種方法差不多。
如果synchronized關鍵字能滿足用戶的需求,就用synchronized,因為它能簡化代碼 。如果需要更高級的功能,就用ReentrantLock類,此時要注意及時釋放鎖,否則會出現死鎖,通常在finally代碼釋放鎖
(5)使用局部變數實現線程同步
Bank.java代碼如下:
packagethreadTest;
/**
*@authorww
*
*/
publicclassBank{
privatestaticThreadLocal<Integer>count=newThreadLocal<Integer>(){
@Override
protectedIntegerinitialValue(){
//TODOAuto-generatedmethodstub
return0;
}
};
//存錢
publicvoidaddMoney(intmoney){
count.set(count.get()+money);
System.out.println(System.currentTimeMillis()+"存進:"+money);
}
//取錢
publicvoidsubMoney(intmoney){
if(count.get()-money<0){
System.out.println("余額不足");
return;
}
count.set(count.get()-money);
System.out.println(+System.currentTimeMillis()+"取出:"+money);
}
//查詢
publicvoidlookMoney(){
System.out.println("賬戶余額:"+count.get());
}
}
運行效果:
余額不足
賬戶余額:0
余額不足
賬戶余額:0
1441794247939存進:100
賬戶余額:100
余額不足
1441794248940存進:100
賬戶余額:0
賬戶余額:200
余額不足
賬戶余額:0
1441794249941存進:100
賬戶余額:300
看了運行效果,一開始一頭霧水,怎麼只讓存,不讓取啊?看看ThreadLocal的原理:
如果使用ThreadLocal管理變數,則每一個使用該變數的線程都獲得該變數的副本,副本之間相互獨立,這樣每一個線程都可以隨意修改自己的變數副本,而不會對其他線程產生影響。現在明白了吧,原來每個線程運行的都是一個副本,也就是說存錢和取錢是兩個賬戶,知識名字相同而已。所以就會發生上面的效果。
ThreadLocal與同步機制
a.ThreadLocal與同步機制都是為了解決多線程中相同變數的訪問沖突問題
b.前者採用以"空間換時間"的方法,後者採用以"時間換空間"的方式