以銀行取錢為例模擬Java多線程同步問題完整代碼
簡單了解下在操作系統(tǒng)中進程和線程的區(qū)別:
進程:每個進程都有獨立的代碼和數(shù)據(jù)空間(進程上下文),進程間的切換會有較大的開銷,一個進程包含1--n個線程。(進程是資源分配的最小單位)
線程:同一類線程共享代碼和數(shù)據(jù)空間,每個線程有獨立的運行棧和程序計數(shù)器(PC),線程切換開銷小。(線程是cpu調(diào)度的最小單位)
線程和進程一樣分為五個階段:創(chuàng)建、就緒、運行、阻塞、終止。
多進程是指操作系統(tǒng)能同時運行多個任務(wù)(程序)。
多線程是指在同一程序中有多個順序流在執(zhí)行。首先存錢取錢的這個操作,應(yīng)該是線程操作的,可以有很多的顧客,這意思就是得有多個線程,多個線程之間共同操作一個銀行,銀行的金額就需要同步。才能保證線程安全。
所以,下面就把這個代碼的實例放這,有不對的地方,還請指出來哈。因為有個老鐵問這個多線程的代碼。
首先是銀行,這個對象model的創(chuàng)建。
package com.lxk.threadTest.bank; /** * 銀行model,一個總金額屬性。 * <p> * * @author lxk on 2017/6/26 */ public class Bank { /** * 給銀行個啟動資金,不然怎么干生意呢。 */ private int sum = 200; //這個從來不這么用,但也算是正確的一種加鎖的機制:同步代碼塊。 //Object obj = new Object(); /** * 存錢 * 要是不加[synchronized--同步函數(shù)],則會出現(xiàn)多線程安全問題。 */ public synchronized void add(int n) { //synchronized (obj) { sum = sum + n; try { Thread.sleep(10); } catch (Exception ignore) { } //當(dāng)存錢次數(shù)變多的時候,就可以發(fā)現(xiàn),存錢的線程確實是2個在交替執(zhí)行存錢這個動作的。 System.out.println(Thread.currentThread().getName() + "...sum=" + sum); //} } /** * 取錢 * 要是不加[synchronized--同步函數(shù)],則會出現(xiàn)多線程安全問題。 */ public synchronized void reduce(int n) { if (sum - n >= 0) { sum = sum - n; } else { System.out.println("bank's money is not enough !"); } try { Thread.sleep(30); } catch (Exception ignore) { } //當(dāng)存錢次數(shù)變多的時候,就可以發(fā)現(xiàn),存錢的線程確實是2個在交替執(zhí)行存錢這個動作的。 System.out.println(Thread.currentThread().getName() + "...sum=" + sum); } }
在代碼里面有存和取2個方法,這2個方法,以及一個總金額,里面有部分被注釋掉的代碼,那個是簡單易懂好理解的,多線程加鎖互斥,保證線程間同步的方法。
但是這個是不常用的方法,常用的就是使用synchronized這個關(guān)鍵字來修飾同步方法。
客戶對象的model
package com.lxk.threadTest.bank; /** * 顧客,實現(xiàn)runnable()接口,多個人可以一起存錢 * * @author lxk on 2017/6/26 */ public class Customer implements Runnable { /** * 存錢類型 */ static final String TYPE_ADD = "add"; /** * 取錢類型 */ static final String TYPE_REDUCE = "reduce"; /** * 銀行 */ private Bank bank; /** * 對錢的操作類型,存錢or取錢 */ private String type; /** * 操作的次數(shù),理論上是個正數(shù) */ private int time; /** * 要存或者取多少錢 */ private int money; public Customer() { } public Customer(Bank bank, String type, int time, int money) { this.bank = bank; this.type = type; this.time = time; this.money = money; } @Override public void run() { for (int x = 0; x < time; x++) { if (TYPE_ADD.equals(type)) { bank.add(money); } else if (TYPE_REDUCE.equals(type)) { bank.reduce(money); } } } }
客戶對象,因為可以很多個客戶同時訪問一個銀行,所以,這個存錢取錢的操作就用線程來實現(xiàn)。
屬性就構(gòu)造方法傳值了。
main方法
package com.lxk.threadTest.bank; /** * 銀行存錢的多線程實例 * <p> * 【需求:】 * 銀行有一個金庫。 * 有兩個儲戶分別存或者取n * 100。 * 目的:該程序是否有安全問題,如果有,如何解決? * <p> * 【如何找問題:】 * 1,明確哪些代碼是多線程運行代碼。 * 2,明確共享數(shù)據(jù)。 * 3,明確多線程運行代碼中哪些語句是操作共享數(shù)據(jù)的。 * * @author lxk on 2017/6/26 */ public class Main { public static void main(String[] args) { //一個銀行and多個客戶 Bank bank = new Bank(); int time = 10000; int money = 100; //這個客戶存錢 Customer c1 = new Customer(bank, Customer.TYPE_ADD, time, money); //這個客戶取錢 Customer c2 = new Customer(bank, Customer.TYPE_REDUCE, time, money); Thread t1 = new Thread(c1); Thread t2 = new Thread(c2); t1.start(); t2.start(); } }
上述代碼實際運行效果如下圖。
這個存取錢的次數(shù)要是小了,就可能會看到2個線程有先后順序,所以,這個次數(shù)咱整多點,然后,就看到如圖所示的情況,線程1是取錢的,線程0時存錢的,可以看到2個線程是互相交錯執(zhí)行的,有存有取,沒有規(guī)律可言。
這個就保證了數(shù)據(jù)的同步了。
至于如何才能不同步,也就是異常的現(xiàn)象,
你可以把add方法的這個synchronized關(guān)鍵字去掉之后,把次數(shù)調(diào)小一點改成3次,sum的初始值給設(shè)置成0.你再試試代碼,
就會發(fā)現(xiàn)所謂的不同步現(xiàn)象。
上圖的右邊就是不同步的結(jié)果,2個人每次存100,存三次,總數(shù)是不是得,100,200,300,400,500,600.得長。
但是,運行結(jié)果卻不是的,
這個時候,你再把synchronized給add方法加上去,就會出現(xiàn)左邊的圖的結(jié)果,這個就是正確的結(jié)果。
我是為了,有存有取,所以,就又加了個方法。代碼就變成上面的樣子啦。
差不多都是線程間同步的例子啦。
我就簡單記錄下代碼。用的時候,可以分分鐘就拿出來。
總結(jié)
以上就是本文關(guān)于以銀行取錢為例模擬Java多線程同步問題完整代碼的全部內(nèi)容,希望對大家有所幫助。感興趣的朋友可以繼續(xù)參閱本站:
如有不足之處,歡迎留言指出。感謝朋友們對本站的支持!
相關(guān)文章
jpa多條件查詢重寫Specification的toPredicate方法
這篇文章主要介紹了多條件查詢重寫Specification的toPredicate方法,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11EL調(diào)用Java方法_動力節(jié)點Java學(xué)院整理
簡單來說,我們在一個類中的某個方法,可以使用EL進行調(diào)用,這個能被EL表達式調(diào)用的方法稱之為EL函數(shù),但是這種方式必須滿足兩點要求,具體哪兩點,大家可以參考下本文2017-07-07Spring Boot基礎(chǔ)入門之基于注解的Mybatis
這篇文章主要給大家介紹了關(guān)于Spring Boot基礎(chǔ)入門之基于注解的Mybatis的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-07-07SpringBoot集成Shiro進行權(quán)限控制和管理的示例
這篇文章主要介紹了SpringBoot集成Shiro進行權(quán)限控制和管理的示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-03-03Spring實現(xiàn)動態(tài)切換多數(shù)據(jù)源的解決方案
這篇文章主要給大家介紹了Spring實現(xiàn)動態(tài)切換多數(shù)據(jù)源的解決方案,文中給出了詳細的介紹和示例代碼,相信對大家的理解和學(xué)習(xí)具有一定的參考借鑒價值,有需要的朋友可以參考學(xué)習(xí),下面來一起看看吧。2017-01-01Java Serializable和Parcelable詳解及實例代碼
這篇文章主要介紹了Java Serializable和Parcelable詳解,并附實例代碼的相關(guān)資料,需要的朋友可以參考下2016-09-09