自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

旺財和小強的三生三世

企業(yè)動態(tài)
旺財和小強是線程池的兩個線程, 他們經(jīng)常做的工作就是對一個數(shù)加加減減,用人類的話來說就是存款,取款。

 ***世

旺財和小強是線程池的兩個線程, 他們經(jīng)常做的工作就是對一個數(shù)加加減減,用人類的話來說就是存款,取款。

  1. public class Account{ 
  2.    private int balance; 
  3.    public synchronized void deposit(int amt){ 
  4.        balance += amt; 
  5.    } 
  6.  
  7.    public synchronized void withdraw(int amt){ 
  8.        if(balance >= amt){ 
  9.            balance -= amt; 
  10.        } 
  11.        throw new RuntimeException("insufficent blance"); 
  12.    } 

(友情提示,可左右滑動,下同)

每次進行存款,取款操作的時候,他們兩個都需要獲得一把鎖,這樣就能保證同一時刻只有一個人在修改,不會出亂子。

這一天,他們倆又遇到了一個叫做轉(zhuǎn)賬的操作:

  1. public void transfer(Account from,Account toint amt){ 
  2.    synchronized(from){ 
  3.        synchronized(to){ 
  4.            from.withdraw(amt); 
  5.            to.deposit(amt); 
  6.        } 
  7.    } 

旺財說:“這個程序員不錯,考慮得挺周全。轉(zhuǎn)賬的時候把兩個賬戶都鎖住了,安全!”

小強說:“沒錯,執(zhí)行吧。”

旺財這個線程從A向B轉(zhuǎn)賬 , 與此同時,小強從B向A轉(zhuǎn)賬

令旺財和小強沒有想到的是,居然出現(xiàn)了死鎖。

 

類似的事件發(fā)生不少, 線程池的線程用光了,Tomcat被迫重啟,這個世界毀滅了。

第二世

新生代的旺財和小強從線程池中出來, Tomcat老大給他們講了上一代旺財和小強的故事, 對他們諄諄教導:“做轉(zhuǎn)賬操作的時候一定要小心,別死鎖了!”

旺財和小強有點兒憤憤不平:“這我們倆也控制不了啊,這要看程序員寫的代碼,以及操作系統(tǒng)中的線程調(diào)度啊!”

不滿歸不滿,他倆還是有點小期待,想看看可怕的轉(zhuǎn)賬代碼到底怎么樣。

沒過多久, 他倆就如愿了:

  1. public static final Object lock = new Object(); 
  2. public void transfer(Account from,Account toint amt){ 
  3.    int fromHash = System.identityHashCode(from); 
  4.    int toHash = System.identityHashCode(to); 
  5.    if(fromHash > toHash){ 
  6.        synchronized(from){ 
  7.            synchronized(to){ 
  8.                from.withdraw(amt); 
  9.                to.deposit(amt); 
  10.            } 
  11.        } 
  12.    } 
  13.    else if(toHash > fromHash){ 
  14.        synchronized(to){ 
  15.            synchronized(from){ 
  16.                from.withdraw(amt); 
  17.                to.deposit(amt); 
  18.            } 
  19.        } 
  20.    } 
  21.    else { 
  22.        synchronized(lock){ 
  23.            synchronized(from){ 
  24.                synchronized(to){ 
  25.                    from.withdraw(amt); 
  26.                    to.deposit(amt); 
  27.                } 
  28.            } 
  29.        } 
  30.    } 

看到這樣的代碼, 旺財?shù)刮艘豢跉?,撓著頭說:“搞什么鬼,轉(zhuǎn)個賬都這么麻煩!”

小強說:“老大不是說了嗎,上一代線程老是在轉(zhuǎn)賬這里出錯,于是代碼就重寫了。你看,這一次寫得就很嚴謹了,每一次都會去比較兩個賬戶的大小(通過hash code),誰大就先獲得誰的鎖。 ”

旺財說:“奧,相當于把賬戶給排了序,假設賬戶A大于賬戶B , 那我們倆轉(zhuǎn)賬的時候,每次都先獲得A的鎖,這樣就不會互相等待了。 ”

 

“沒錯,還有一個特殊情況,如果這兩個賬戶的hash code 相同,那就再去競爭另外一個特殊的鎖,誰搶到誰就可以先執(zhí)行。另一個就在那里等待。”

旺財和小強這次順利地把轉(zhuǎn)賬給執(zhí)行完了,回去給Tomcat匯報了一遍。

Tomcat老大感慨地說:“有這么復雜的代碼,可見使用‘共享內(nèi)存’的方式來并發(fā)編程很不容易啊!”

“共享內(nèi)存?”

“對啊,你看這些賬戶的數(shù)據(jù),每個線程都可以訪問,不就是共享內(nèi)存嗎, 為了能夠安全訪問,只有來‘加鎖’了。 古人說,這個世界上有兩種構建軟件的方式,一種方法是使其足夠簡單以至于不存在明顯的缺陷,另外一種是使其足夠復雜以至于看不出有什么問題。我很擔心啊, 現(xiàn)在這個系統(tǒng)就屬于第二種,不知道有多少坑在等著我們呢!”

(碼農(nóng)翻身: 實際上這句話是托尼·霍爾說的)

老大不幸言中,終于有一天,這個復雜到看不出問題的系統(tǒng)崩潰了,這個世界又毀滅了。

第三世

第三代的旺財和小強從線程池出來。

出發(fā)前,Tomcat老大把前兩代線程遇到的問題給他們說了一遍,威脅說:如果再出現(xiàn)死鎖,小心你們兩個的腦袋!

旺財和小強戰(zhàn)戰(zhàn)兢兢,如履薄冰地執(zhí)行代碼。

最終他們還是遇到了傳說中的可怕的轉(zhuǎn)賬代碼:

  1. def transfer(from: Account, to: Account,amt:Int){ 
  2.    atomic{ 
  3.        from.withdraw(amt); 
  4.        to.deposit(amt); 
  5.    } 

旺財非常吃驚:“這是什么代碼?不是Java?”

小強說:“嗯,不是Java ,是Scala寫的,這是運行在JVM上的一個語言。”

(碼農(nóng)翻身注:實際上JVM線程能看到的只是Java 字節(jié)碼,根本看不到源碼,也就不知道是Java寫的代碼,還是Scala寫的代碼, 這里只是為了展示方便。)

旺財說:“怎么這么簡單,會不會出問題?那個atomic是怎么回事?表示原子執(zhí)行?”

小強也有點懵,不敢貿(mào)然去執(zhí)行:“咱們還是去問Tomcat老大吧。”

Tomcat看了一眼:“人類程序員又改代碼了啊,開始使用Software Transaction Memory(STM)了。 去把STM老頭兒叫來,讓他給你倆解釋。”

STM老頭兒滿臉滄桑:“放心執(zhí)行吧,只要你把代碼放到atomic中,我就能保證他們像事務一樣,實現(xiàn)ACID,哦不,D(持久化)實現(xiàn)不了,這些數(shù)據(jù)都是在內(nèi)存中的。”

“這有什么用? ”

“可以讓你們倆安全地并發(fā)執(zhí)行啊?”

旺財和小強面面相覷,這連鎖都沒有,還安全地并發(fā)?

“別看沒有鎖,” STM老頭兒說,“在atomic代碼開始執(zhí)行的時候,我會記錄下代碼塊涉及到的數(shù)據(jù)的值(復制了一份),然后才真正執(zhí)行,執(zhí)行完了要‘提交’, 這時候我會看看那些數(shù)據(jù)的值是否也被別的線程改動了,如果有改動,那本次改動就撤銷,重新從代碼開始處執(zhí)行。 ”

老頭兒畫了一個圖,展示旺財從賬戶A給賬戶B轉(zhuǎn)20元, 與此同時小強從B向A轉(zhuǎn)30元。

 

還真是,沒有加鎖就安全地完成了兩個并發(fā)操作。

當然,老頭兒為了實現(xiàn)這個atomic操作,背后偷偷做了不少事情:復制數(shù)據(jù),提交,重復執(zhí)行。

旺財想起來自己曾經(jīng)執(zhí)行過一下Java 的Compare and swap的代碼,說道:“你這不就是CAS嘛!”

老頭兒說:“原理上類似,都是樂觀鎖,不過我這個方式和數(shù)據(jù)庫的事務更加類似,所以叫做Software Transaction Memory。”

小強想了想,說道:“不對啊,atomic是個代碼塊,里邊可能有很多代碼,涉及到很多class, 你怎么知道哪些字段需要被STM管理起來啊!”

STM老頭兒說:“這真是個好問題,實際上,需要程序員們來告訴我。比如使用這個方法”

  1. class Account(val initialBalance : Int){ 
  2.  val balance = Ref(initialBalance) 
  3.  ...... 

“看到那個Ref沒有,這就是一種辦法,通過它,我就知道這個balance的字段需要讓我管理起來,在atomic代碼塊運行的時候,就需要復制它的值,比較它的值。”

“明白了,但是‘重復執(zhí)行’有問題啊,假設程序員張大胖是這么寫代碼的:”

  1. def transfer(from: Account, to: Account,amt:Int){ 
  2.     atomic{ 
  3.         from.withdraw(amt); 
  4.         ...在這里執(zhí)行一些其他操作,例如打印日志,發(fā)送郵件..... 
  5.         to.deposit(amt); 
  6.     } 

“這其中有一些打印日志,發(fā)送郵件的操作,那你重復執(zhí)行,豈不會執(zhí)行很多次,就完全亂套了。”

STM老頭兒說:“不錯,想得挺深,你說的這些操作,我把他們叫做副作用,不能重復執(zhí)行,不能放到atomic代碼塊中讓STM管理。換句話說atomic中的代碼應該是冪等的。如果違背了這一點,后果自負!”

小強心中一凜:“這是程序員要操心的事情了,不管我倆的事情, 不過即使如此,他們的代碼也極度地簡化了,只需要用個atomic,就能實現(xiàn)安全地并發(fā),實在是太爽了。”

旺財說道:“你說得天花亂墜,這STM有什么缺點?”

老頭兒說:“天下沒有免費的午餐,很容易想到STM的局限性, 如果對于同一個數(shù)據(jù),并發(fā)寫入很多的時候,沖突就大大增加了,不斷地重復執(zhí)行,效率很低。所以更適合寫入少,讀取多的場景。”

“好吧,我們這就執(zhí)行這個轉(zhuǎn)賬操作,有問題就找你!”

【本文為51CTO專欄作者“劉欣”的原創(chuàng)稿件,轉(zhuǎn)載請通過作者微信公眾號coderising獲取授權】

 

戳這里,看該作者更多好文

責任編輯:武曉燕 來源: 51CTO專欄
相關推薦

2021-11-23 23:13:11

數(shù)據(jù)庫安全工具

2019-02-12 09:45:27

2018-06-14 10:20:20

2009-06-18 16:54:11

Fedora 11910

2010-09-17 10:45:04

李彥宏

2019-06-27 17:30:46

貝銳科技網(wǎng)絡傳輸

2022-06-02 07:36:14

5G5G商用

2017-10-11 09:04:16

服務器劫難死機

2015-06-02 16:16:56

GoogleAndroid M

2016-08-02 11:03:22

數(shù)字 系列

2016-09-13 15:24:24

孟憲坤 思維

2018-06-05 15:41:22

進程線程協(xié)程

2010-08-20 09:02:57

菲律賓總統(tǒng)互聯(lián)網(wǎng)

2020-11-16 17:50:21

華為北京聯(lián)通5G

2014-01-17 17:20:18

昆騰數(shù)據(jù)保護大數(shù)據(jù)管理

2013-07-24 10:24:55

蘋果iPhone財報

2014-05-04 14:15:31

點贊
收藏

51CTO技術棧公眾號