線程同步主要有以下種方法(示例中是實現(xiàn)計數(shù)的功能):
十年的恭城網(wǎng)站建設(shè)經(jīng)驗,針對設(shè)計、前端、開發(fā)、售后、文案、推廣等六對一服務(wù),響應(yīng)快,48小時及時工作處理。全網(wǎng)整合營銷推廣的優(yōu)勢是能夠根據(jù)用戶設(shè)備顯示端的尺寸不同,自動調(diào)整恭城建站的顯示方式,使網(wǎng)站能夠適用不同顯示終端,在瀏覽器中調(diào)整網(wǎng)站的寬度,無論在任何一種瀏覽器上瀏覽網(wǎng)站,都能展現(xiàn)優(yōu)雅布局與設(shè)計,從而大程度地提升瀏覽體驗。成都創(chuàng)新互聯(lián)從事“恭城網(wǎng)站設(shè)計”,“恭城網(wǎng)站推廣”以來,每個客戶項目都認(rèn)真落實執(zhí)行。
1、同步方法,即使用synchronized關(guān)鍵字修飾方法,例如:
public?synchronized?void?add(int?c){...}
2、同步代碼塊,即有synchronized關(guān)鍵字修飾的語句塊,例如:
public?void?addAndGet(int?c){
synchronized(this){
count?+=?c;
}
}
3、使用特殊域變量(volatile)實現(xiàn)線程同步,該方法不能保證絕對的同步。
例如:private?volatile?int?count?=?0;
4、使用鎖實現(xiàn)線程同步,例如:
private?Lock?lock?=?new?ReentrantLock();
public?void?add(int?c)?{??
lock.lock();//上鎖??
try{??
count?+=?c;??
}finally{??
lock.unlock();//解鎖??
}??
}
5、使用原子變量實現(xiàn)線程同步,在java的util.concurrent.atomic包中提供了創(chuàng)建了原子類型變量的工具類,例如:
private?AtomicInteger?count=?new?AtomicInteger(1);
public?void?add(int?c)?{
count.addAndGet(c);
}
6、使用局部變量實現(xiàn)線程同步,如果使用ThreadLocal管理變量,則每一個使用該變量的線程都獲得該變量的副本, 副本之間相互獨立,這樣每一個線程都可以隨意修改自己的變量副本,而不會對其他線程產(chǎn)生影響。
ThreadLocal 類的常用方法
new ThreadLocalT() : 創(chuàng)建一個線程本地變量
get() : 返回此線程局部變量的當(dāng)前線程副本中的值
initialValue() : 返回此線程局部變量的當(dāng)前線程的"初始值"
set(T value) : 將此線程局部變量的當(dāng)前線程副本中的值設(shè)置為value
示例代碼:
private?static?ThreadLocalInteger?count=?new?ThreadLocalInteger(){
@Override
protected?Integer?initialValue(){?
return?1;
}
};????????????
public?void?add(int?c){
count.set(count.get()?+?c);
}
7、使用阻塞隊列實現(xiàn),例如LinkedBlockingQueue,具體使用可百度LinkedBlockingQueue的用法或查看java文檔。
要想解決 臟數(shù)據(jù) 的問題 最簡單的方法就是使用synchronized關(guān)鍵字來使run方法同步 代碼如下
public?synchronized?void?run(){?????}
從上面的代碼可以看出 只要在void和public之間加上synchronized關(guān)鍵字 就可以使run方法同步 也就是說 對于同一個Java類的對象實例 run方法同時只能被一個線程調(diào)用 并當(dāng)前的run執(zhí)行完后 才能被其他的線程調(diào)用 即使當(dāng)前線程執(zhí)行到了run方法中的yield方法 也只是暫停了一下 由于其他線程無法執(zhí)行run方法 因此 最終還是會由當(dāng)前的線程來繼續(xù)執(zhí)行 先看看下面的代碼
sychronized關(guān)鍵字只和一個對象實例綁定
class?Test??{????????public?synchronized?void?method()???????{???????????????????}??}?????public?class?Sync?implements?Runnable??{???????private?Test?test;???????public?void?run()?????? {????????? ? thod();?????? }???? ? public?Sync(Test?test)???? ? {???????? ? this test?=?test;?????? }?? ? ? public?static?void?main(String[]?args)?throws?Exception?? ? ? {???????? ? Test?test ?=??new?Test();??????? ?? Test?test ?=??new?Test();?????????? Sync?sync ?=?new?Sync(test );???????? ? Sync?sync ?=?new?Sync(test );???????? ? new?Thread(sync ) start();???????? ? new?Thread(sync ) start();??? ? ? }?? }
在Test類中的method方法是同步的 但上面的代碼建立了兩個Test類的實例 因此 test 和test 的method方法是分別執(zhí)行的 要想讓method同步 必須在建立Sync類的實例時向它的構(gòu)造方法中傳入同一個Test類的實例 如下面的代碼所示
Sync?sync ?=?new?Sync(test );
不僅可以使用synchronized來同步非靜態(tài)方法 也可以使用synchronized來同步靜態(tài)方法 如可以按如下方式來定義method方法
class?Test?{??? public?static?synchronized?void?method()?{???}}
建立Test類的對象實例如下
Test?test?=?new?Test();
對于靜態(tài)方法來說 只要加上了synchronized關(guān)鍵字 這個方法就是同步的 無論是使用thod() 還是使用thod()來調(diào)用method方法 method都是同步的 并不存在非靜態(tài)方法的多個實例的問題
在 種設(shè)計模式中的單件(Singleton)模式如果按傳統(tǒng)的方法設(shè)計 也是線程不安全的 下面的代碼是一個線程不安全的單件模式
package?test;//?線程安全的Singleton模式class?Singleton{????private?static?Singleton?sample;????private?Singleton()????{????}????public?static?Singleton?getInstance()????{????????if?(sample?==?null)????????{????????????Thread yield();?//?為了放大Singleton模式的線程不安全性????????????sample?=?new?Singleton();????????}????????return?sample;????}}public?class?MyThread?extends?Thread{????public?void?run()????{????????Singleton?singleton?=?Singleton getInstance();????????System out println(singleton hashCode());????}????public?static?void?main(String[]?args)????{????????Thread?threads[]?=?new?Thread[ ];????????for?(int?i?=? ;?i??threads length;?i++)????????????threads[i]?=?new?MyThread();????????for?(int?i?=? ;?i??threads length;?i++)????????????threads[i] start();????}}
在上面的代碼調(diào)用yield方法是為了使單件模式的線程不安全性表現(xiàn)出來 如果將這行去掉 上面的實現(xiàn)仍然是線程不安全的 只是出現(xiàn)的可能性小得多
程序的運行結(jié)果如下
上面的運行結(jié)果可能在不同的運行環(huán)境上有所有同 但一般這五行輸出不會完全相同 從這個輸出結(jié)果可以看出 通過getInstance方法得到的對象實例是五個 而不是我們期望的一個 這是因為當(dāng)一個線程執(zhí)行了Thread yield()后 就將CPU資源交給了另外一個線程 由于在線程之間切換時并未執(zhí)行到創(chuàng)建Singleton對象實例的語句 因此 這幾個線程都通過了if判斷 所以 就會產(chǎn)生了建立五個對象實例的情況(可能創(chuàng)建的是四個或三個對象實例 這取決于有多少個線程在創(chuàng)建Singleton對象之前通過了if判斷 每次運行時可能結(jié)果會不一樣)
要想使上面的單件模式變成線程安全的 只要為getInstance加上synchronized關(guān)鍵字即可 代碼如下
public?static?synchronized?Singleton?getInstance()?{???}
當(dāng)然 還有更簡單的方法 就是在定義Singleton變量時就建立Singleton對象 代碼如下
private?static?final?Singleton?sample?=?new?Singleton();
然后在getInstance方法中直接將sample返回即可 這種方式雖然簡單 但不知在getInstance方法中創(chuàng)建Singleton對象靈活 讀者可以根據(jù)具體的需求選擇使用不同的方法來實現(xiàn)單件模式
在使用synchronized關(guān)鍵字時有以下四點需要注意
synchronized關(guān)鍵字不能繼承
雖然可以使用synchronized來定義方法 但synchronized并不屬于方法定義的一部分 因此 synchronized關(guān)鍵字不能被繼承 如果在父類中的某個方法使用了synchronized關(guān)鍵字 而在子類中覆蓋了這個方法 在子類中的這個方法默認(rèn)情況下并不是同步的 而必須顯式地在子類的這個方法中加上synchronized關(guān)鍵字才可以 當(dāng)然 還可以在子類方法中調(diào)用父類中相應(yīng)的方法 這樣雖然子類中的方法不是同步的 但子類調(diào)用了父類的同步方法 因此 子類的方法也就相當(dāng)于同步了 這兩種方式的例子代碼如下
在子類方法中加上synchronized關(guān)鍵字
class?Parent{??? public?synchronized?void?method()?{???}}class?Child?extends?Parent{??? public?synchronized?void?method()?{???}}
在子類方法中調(diào)用父類的同步方法
class?Parent{??? public?synchronized?void?method()?{???}}class?Child?extends?Parent{????public?void?method()?{?thod();???}}
在定義接口方法時不能使用synchronized關(guān)鍵字
構(gòu)造方法不能使用synchronized關(guān)鍵字 但可以使用下節(jié)要討論的synchronized塊來進(jìn)行同步
synchronized可以自由放置
在前面的例子中使用都是將synchronized關(guān)鍵字放在方法的返回類型前面 但這并不是synchronized可放置唯一位置 在非靜態(tài)方法中 synchronized還可以放在方法定義的最前面 在靜態(tài)方法中 synchronized可以放在static的前面 代碼如下
public?synchronized?void?method();synchronized?public?void?method();public?static?synchronized?void?method();public?synchronized?static?void?method();synchronized?public?static?void?method();
但要注意 synchronized不能放在方法返回類型的后面 如下面的代碼是錯誤的
public?void?synchronized?method();public?static?void?synchronized?method();
synchronized關(guān)鍵字只能用來同步方法 不能用來同步類變量 如下面的代碼也是錯誤的
public?synchronized?int?n?=? ;public?static?synchronized?int?n?=? ;
雖然使用synchronized關(guān)鍵字同步方法是最安全的同步方式 但大量使用synchronized關(guān)鍵字會造成不必要的資源消耗以及性能損失 雖然從表面上看synchronized鎖定的是一個方法 但實際上synchronized鎖定的是一個類 也就是說 如果在非靜態(tài)方法method 和method 定義時都使用了synchronized 在method 未執(zhí)行完之前 method 是不能執(zhí)行的 靜態(tài)方法和非靜態(tài)方法的情況類似 但靜態(tài)和非靜態(tài)方法不會互相影響 看看如下的代碼
package?test;public?class?MyThread ?extends?Thread{????public?String?methodName;????public?static?void?method(String?s)????{????????System out println(s);????????while?(true)????????????;????}????public?synchronized?void?method ()????{????????method( 非靜態(tài)的method 方法 );????}????public?synchronized?void?method ()????{????????method( 非靜態(tài)的method 方法 );????}????public?static?synchronized?void?method ()????{????????method( 靜態(tài)的method 方法 );????}????public?static?synchronized?void?method ()????{????????method( 靜態(tài)的method 方法 );????}????public?void?run()????{????????try????????{????????????getClass() getMethod(methodName) invoke(this);????????}????????catch?(Exception?e)????????{????????}????}????public?static?void?main(String[]?args)?throws?Exception????{????????MyThread ?myThread ?=?new?MyThread ();????????for?(int?i?=? ;?i?=? ;?i++)????????{????????????thodName?=? method ?+?String valueOf(i);????????????new?Thread(myThread ) start();????????????sleep( );????????}????}}
運行結(jié)果如下
非靜態(tài)的method 方法靜態(tài)的method 方法
lishixinzhi/Article/program/Java/gj/201311/27526
synchronized關(guān)鍵字有兩種用法 第一種就是在《使用Synchronized關(guān)鍵字同步類方法》一文中所介紹的直接用在方法的定義中 另外一種就是synchronized塊 我們不僅可以通過synchronized塊來同步一個對象變量 也可以使用synchronized塊來同步類中的靜態(tài)方法和非靜態(tài)方法
synchronized塊的語法如下
public void method()
{
… …
synchronized(表達(dá)式)
{
… …
}
}
一 非靜態(tài)類方法的同步
從《使用Synchronized關(guān)鍵字同步類方法》一文中我們知道使用synchronized關(guān)鍵字來定義方法就會鎖定類中所有使用synchronzied關(guān)鍵字定義的靜態(tài)方法或非靜態(tài)方法 但這并不好理解 而如果使用synchronized塊來達(dá)到同樣的效果 就不難理解為什么會產(chǎn)生這種效果了 如果想使用synchronized塊來鎖定類中所有的同步非靜態(tài)方法 需要使用this做為synchronized塊的參數(shù)傳入synchronized塊國 代碼如下
通過synchronized塊同步非靜態(tài)方法
public class SyncBlock
{
public void method ()
{
synchronized(this)? // 相當(dāng)于對method 方法使用synchronized關(guān)鍵字
{
… …
}
}
public void method ()
{
synchronized(this)? // 相當(dāng)于對method 方法使用synchronized關(guān)鍵字
{
… …
}
}
public synchronized void method ()
{
… …
}
}
在上面的代碼中的method 和method 方法中使用了synchronized塊 而第 行的method 方法仍然使用synchronized關(guān)鍵字來定義方法 在使用同一個SyncBlock類實例時 這三個方法只要有一個正在執(zhí)行 其他兩個方法就會因未獲得同步鎖而被阻塞 在使用synchronized塊時要想達(dá)到和synchronized關(guān)鍵字同樣的效果 必須將所有的代碼都寫在synchronized塊中 否則 將無法使當(dāng)前方法中的所有代碼和其他的方法同步
除了使用this做為synchronized塊的參數(shù)外 還可以使用SyncBlock this作為synchronized塊的參數(shù)來達(dá)到同樣的效果
在內(nèi)類(InnerClass)的方法中使用synchronized塊來時 this只表示內(nèi)類 和外類(OuterClass)沒有關(guān)系 但內(nèi)類的非靜態(tài)方法可以和外類的非靜態(tài)方法同步 如在內(nèi)類InnerClass中加一個method 方法 并使method 方法和SyncBlock的三個方法同步 代碼如下
使內(nèi)類的非靜態(tài)方法和外類的非靜態(tài)方法同步
public class SyncBlock
{
… …
class InnerClass
{
public void method ()
{
synchronized(SyncBlock this)
{
… …
}
}
}
… …
}
在上面SyncBlock類的新版本中 InnerClass類的method 方法和SyncBlock類的其他三個方法同步 因此 method method method 和method 四個方法在同一時間只能有一個方法執(zhí)行
Synchronized塊不管是正常執(zhí)行完 還是因為程序出錯而異常退出synchronized塊 當(dāng)前的synchronized塊所持有的同步鎖都會自動釋放 因此 在使用synchronized塊時不必?fù)?dān)心同步鎖的釋放問題
二 靜態(tài)類方法的同步
由于在調(diào)用靜態(tài)方法時 對象實例不一定被創(chuàng)建 因此 就不能使用this來同步靜態(tài)方法 而必須使用Class對象來同步靜態(tài)方法 代碼如下
通過synchronized塊同步靜態(tài)方法
public class StaticSyncBlock
{
public static void method ()
{
synchronized(StaticSyncBlock class)
{
… …
}
}
public static synchronized void method ()
{
… …
}
}
在同步靜態(tài)方法時可以使用類的靜態(tài)字段class來得到Class對象 在上例中method 和method 方法同時只能有一個方法執(zhí)行 除了使用class字段得到Class對象外 還可以使用實例的getClass方法來得到Class對象 上例中的代碼可以修改如下
使用getClass方法得到Class對象
public class StaticSyncBlock
{
public static StaticSyncBlock instance;
public StaticSyncBlock()
{
instance = this;
}
public static void method ()
{
synchronized(instance getClass())
{
}
}
}
在上面代碼中通過一個public的靜態(tài)instance得到一個StaticSyncBlock類的實例 并通過這個實例的getClass方法得到了Class對象(一個類的所有實例通過getClass方法得到的都是同一個Class對象 因此 調(diào)用任何一個實例的getClass方法都可以) 我們還可以通過Class對象使不同類的靜態(tài)方法同步 如Test類的靜態(tài)方法method和StaticSyncBlock類的兩個靜態(tài)方法同步 代碼如下
Test類的method方法和StaticSyncBlock類的method method 方法同步
public class Test
{
public static void method()
{
synchronized(StaticSyncBlock class)
{
}
}
}
lishixinzhi/Article/program/Java/gj/201311/27374
一、為什么要線程同步
因為當(dāng)我們有多個線程要同時訪問一個變量或?qū)ο髸r,如果這些線程中既有讀又有寫操作時,就會導(dǎo)致變量值或?qū)ο蟮臓顟B(tài)出現(xiàn)混亂,從而導(dǎo)致程序異常。舉個例子,如果一個銀行賬戶同時被兩個線程操作,一個取100塊,一個存錢100塊。假設(shè)賬戶原本有0塊,如果取錢線程和存錢線程同時發(fā)生,會出現(xiàn)什么結(jié)果呢?取錢不成功,賬戶余額是100.取錢成功了,賬戶余額是0.那到底是哪個呢?很難說清楚。因此多線程同步就是要解決這個問題。
二、不同步時的代碼
Bank.Java
package?threadTest;
/**
*?@author?ww
*
*/
public?class?Bank?{
private?int?count?=0;//賬戶余額
//存錢
public??void?addMoney(int?money){
count?+=money;
System.out.println(System.currentTimeMillis()+"存進(jìn):"+money);
}
//取錢
public??void?subMoney(int?money){
if(count-money??0){
System.out.println("余額不足");
return;
}
count?-=money;
System.out.println(+System.currentTimeMillis()+"取出:"+money);
}
//查詢
public?void?lookMoney(){
System.out.println("賬戶余額:"+count);
}
}
SyncThreadTest.java
package?threadTest;
public?class?SyncThreadTest?{
public?static?void?main(String?args[]){
final?Bank?bank=new?Bank();
Thread?tadd=new?Thread(new?Runnable()?{
@Override
public?void?run()?{
//?TODO?Auto-generated?method?stub
while(true){
try?{
Thread.sleep(1000);
}?catch?(InterruptedException?e)?{
//?TODO?Auto-generated?catch?block
e.printStackTrace();
}
bank.addMoney(100);
bank.lookMoney();
System.out.println("\n");
}
}
});
Thread?tsub?=?new?Thread(new?Runnable()?{
@Override
public?void?run()?{
//?TODO?Auto-generated?method?stub
while(true){
bank.subMoney(100);
bank.lookMoney();
System.out.println("\n");
try?{
Thread.sleep(1000);
}?catch?(InterruptedException?e)?{
//?TODO?Auto-generated?catch?block
e.printStackTrace();
}
}
}
});
tsub.start();
tadd.start();
}
}
余額不足
賬戶余額:0
余額不足
賬戶余額:100
1441790503354存進(jìn):100
賬戶余額:100
1441790504354存進(jìn):100
賬戶余額:100
1441790504354取出:100
賬戶余額:100
1441790505355存進(jìn):100
賬戶余額:100
1441790505355取出:100
賬戶余額:100
三、使用同步時的代碼
(1)同步方法:
即有synchronized關(guān)鍵字修飾的方法。?由于java的每個對象都有一個內(nèi)置鎖,當(dāng)用此關(guān)鍵字修飾方法時,內(nèi)置鎖會保護(hù)整個方法。在調(diào)用該方法前,需要獲得內(nèi)置鎖,否則就處于阻塞狀態(tài)。
修改后的Bank.java
package?threadTest;
/**
*?@author?ww
*
*/
public?class?Bank?{
private?int?count?=0;//賬戶余額
//存錢
public??synchronized?void?addMoney(int?money){
count?+=money;
System.out.println(System.currentTimeMillis()+"存進(jìn):"+money);
}
//取錢
public??synchronized?void?subMoney(int?money){
if(count-money??0){
System.out.println("余額不足");
return;
}
count?-=money;
System.out.println(+System.currentTimeMillis()+"取出:"+money);
}
//查詢
public?void?lookMoney(){
System.out.println("賬戶余額:"+count);
}
}
再看看運行結(jié)果:
余額不足
賬戶余額:0
余額不足
賬戶余額:0
1441790837380存進(jìn):100
賬戶余額:100
1441790838380取出:100
賬戶余額:0
1441790838380存進(jìn):100
賬戶余額:100
1441790839381取出:100
賬戶余額:0
瞬間感覺可以理解了吧。
注: synchronized關(guān)鍵字也可以修飾靜態(tài)方法,此時如果調(diào)用該靜態(tài)方法,將會鎖住整個類
(2)同步代碼塊
即有synchronized關(guān)鍵字修飾的語句塊。被該關(guān)鍵字修飾的語句塊會自動被加上內(nèi)置鎖,從而實現(xiàn)同步
Bank.java代碼如下:
package?threadTest;
/**
*?@author?ww
*
*/
public?class?Bank?{
private?int?count?=0;//賬戶余額
//存錢
public???void?addMoney(int?money){
synchronized?(this)?{
count?+=money;
}
System.out.println(System.currentTimeMillis()+"存進(jìn):"+money);
}
//取錢
public???void?subMoney(int?money){
synchronized?(this)?{
if(count-money??0){
System.out.println("余額不足");
return;
}
count?-=money;
}
System.out.println(+System.currentTimeMillis()+"取出:"+money);
}
//查詢
public?void?lookMoney(){
System.out.println("賬戶余額:"+count);
}
}
運行結(jié)果如下:
余額不足??
賬戶余額:0??
1441791806699存進(jìn):100??
賬戶余額:100??
1441791806700取出:100??
賬戶余額:0??
1441791807699存進(jìn):100??
賬戶余額:100
效果和方法一差不多。
注:同步是一種高開銷的操作,因此應(yīng)該盡量減少同步的內(nèi)容。通常沒有必要同步整個方法,使用synchronized代碼塊同步關(guān)鍵代碼即可。
(3)使用特殊域變量(volatile)實現(xiàn)線程同步
a.volatile關(guān)鍵字為域變量的訪問提供了一種免鎖機(jī)制
b.使用volatile修飾域相當(dāng)于告訴虛擬機(jī)該域可能會被其他線程更新
c.因此每次使用該域就要重新計算,而不是使用寄存器中的值
d.volatile不會提供任何原子操作,它也不能用來修飾final類型的變量
Bank.java代碼如下:
package?threadTest;
/**
*?@author?ww
*
*/
public?class?Bank?{
private?volatile?int?count?=?0;//?賬戶余額
//?存錢
public?void?addMoney(int?money)?{
count?+=?money;
System.out.println(System.currentTimeMillis()?+?"存進(jìn):"?+?money);
}
//?取錢
public?void?subMoney(int?money)?{
if?(count?-?money??0)?{
System.out.println("余額不足");
return;
}
count?-=?money;
System.out.println(+System.currentTimeMillis()?+?"取出:"?+?money);
}
//?查詢
public?void?lookMoney()?{
System.out.println("賬戶余額:"?+?count);
}
}
運行效果怎樣呢?
余額不足
賬戶余額:0
余額不足
賬戶余額:100
1441792010959存進(jìn):100
賬戶余額:100
1441792011960取出:100
賬戶余額:0
1441792011961存進(jìn):100
賬戶余額:100
是不是又看不懂了,又亂了。這是為什么呢?就是因為volatile不能保證原子操作導(dǎo)致的,因此volatile不能代替synchronized。此外volatile會組織編譯器對代碼優(yōu)化,因此能不使用它就不適用它吧。它的原理是每次要線程要訪問volatile修飾的變量時都是從內(nèi)存中讀取,而不是存緩存當(dāng)中讀取,因此每個線程訪問到的變量值都是一樣的。這樣就保證了同步。
(4)使用重入鎖實現(xiàn)線程同步
在JavaSE5.0中新增了一個java.util.concurrent包來支持同步。ReentrantLock類是可重入、互斥、實現(xiàn)了Lock接口的鎖,?它與使用synchronized方法和快具有相同的基本行為和語義,并且擴(kuò)展了其能力。
ReenreantLock類的常用方法有:
ReentrantLock() : 創(chuàng)建一個ReentrantLock實例
lock() : 獲得鎖
unlock() : 釋放鎖
注:ReentrantLock()還有一個可以創(chuàng)建公平鎖的構(gòu)造方法,但由于能大幅度降低程序運行效率,不推薦使用?
Bank.java代碼修改如下:
package?threadTest;
import?java.util.concurrent.locks.Lock;
import?java.util.concurrent.locks.ReentrantLock;
/**
*?@author?ww
*
*/
public?class?Bank?{
private??int?count?=?0;//?賬戶余額
//需要聲明這個鎖
private?Lock?lock?=?new?ReentrantLock();
//?存錢
public?void?addMoney(int?money)?{
lock.lock();//上鎖
try{
count?+=?money;
System.out.println(System.currentTimeMillis()?+?"存進(jìn):"?+?money);
}finally{
lock.unlock();//解鎖
}
}
//?取錢
public?void?subMoney(int?money)?{
lock.lock();
try{
if?(count?-?money??0)?{
System.out.println("余額不足");
return;
}
count?-=?money;
System.out.println(+System.currentTimeMillis()?+?"取出:"?+?money);
}finally{
lock.unlock();
}
}
//?查詢
public?void?lookMoney()?{
System.out.println("賬戶余額:"?+?count);
}
}
運行效果怎么樣呢?
余額不足
賬戶余額:0
余額不足
賬戶余額:0
1441792891934存進(jìn):100
賬戶余額:100
1441792892935存進(jìn):100
賬戶余額:200
1441792892954取出:100
賬戶余額:100
效果和前兩種方法差不多。
如果synchronized關(guān)鍵字能滿足用戶的需求,就用synchronized,因為它能簡化代碼 。如果需要更高級的功能,就用ReentrantLock類,此時要注意及時釋放鎖,否則會出現(xiàn)死鎖,通常在finally代碼釋放鎖?
(5)使用局部變量實現(xiàn)線程同步
Bank.java代碼如下:
package?threadTest;
/**
*?@author?ww
*
*/
public?class?Bank?{
private?static?ThreadLocalInteger?count?=?new?ThreadLocalInteger(){
@Override
protected?Integer?initialValue()?{
//?TODO?Auto-generated?method?stub
return?0;
}
};
//?存錢
public?void?addMoney(int?money)?{
count.set(count.get()+money);
System.out.println(System.currentTimeMillis()?+?"存進(jìn):"?+?money);
}
//?取錢
public?void?subMoney(int?money)?{
if?(count.get()?-?money??0)?{
System.out.println("余額不足");
return;
}
count.set(count.get()-?money);
System.out.println(+System.currentTimeMillis()?+?"取出:"?+?money);
}
//?查詢
public?void?lookMoney()?{
System.out.println("賬戶余額:"?+?count.get());
}
}
運行效果:
余額不足
賬戶余額:0
余額不足
賬戶余額:0
1441794247939存進(jìn):100
賬戶余額:100
余額不足
1441794248940存進(jìn):100
賬戶余額:0
賬戶余額:200
余額不足
賬戶余額:0
1441794249941存進(jìn):100
賬戶余額:300
看了運行效果,一開始一頭霧水,怎么只讓存,不讓取?。靠纯碩hreadLocal的原理:
如果使用ThreadLocal管理變量,則每一個使用該變量的線程都獲得該變量的副本,副本之間相互獨立,這樣每一個線程都可以隨意修改自己的變量副本,而不會對其他線程產(chǎn)生影響。現(xiàn)在明白了吧,原來每個線程運行的都是一個副本,也就是說存錢和取錢是兩個賬戶,知識名字相同而已。所以就會發(fā)生上面的效果。
ThreadLocal與同步機(jī)制?
a.ThreadLocal與同步機(jī)制都是為了解決多線程中相同變量的訪問沖突問題
b.前者采用以"空間換時間"的方法,后者采用以"時間換空間"的方式