以銀行取錢為例模擬Java多線程同步問題完整代碼
簡(jiǎn)單了解下在操作系統(tǒng)中進(jìn)程和線程的區(qū)別:
進(jìn)程:每個(gè)進(jìn)程都有獨(dú)立的代碼和數(shù)據(jù)空間(進(jìn)程上下文),進(jìn)程間的切換會(huì)有較大的開銷,一個(gè)進(jìn)程包含1--n個(gè)線程。(進(jìn)程是資源分配的最小單位)
線程:同一類線程共享代碼和數(shù)據(jù)空間,每個(gè)線程有獨(dú)立的運(yùn)行棧和程序計(jì)數(shù)器(PC),線程切換開銷小。(線程是cpu調(diào)度的最小單位)
線程和進(jìn)程一樣分為五個(gè)階段:創(chuàng)建、就緒、運(yùn)行、阻塞、終止。
多進(jìn)程是指操作系統(tǒng)能同時(shí)運(yùn)行多個(gè)任務(wù)(程序)。
多線程是指在同一程序中有多個(gè)順序流在執(zhí)行。首先存錢取錢的這個(gè)操作,應(yīng)該是線程操作的,可以有很多的顧客,這意思就是得有多個(gè)線程,多個(gè)線程之間共同操作一個(gè)銀行,銀行的金額就需要同步。才能保證線程安全。
所以,下面就把這個(gè)代碼的實(shí)例放這,有不對(duì)的地方,還請(qǐng)指出來哈。因?yàn)橛袀€(gè)老鐵問這個(gè)多線程的代碼。
首先是銀行,這個(gè)對(duì)象model的創(chuàng)建。
package com.lxk.threadTest.bank;
/**
* 銀行model,一個(gè)總金額屬性。
* <p>
*
* @author lxk on 2017/6/26
*/
public class Bank {
/**
* 給銀行個(gè)啟動(dòng)資金,不然怎么干生意呢。
*/
private int sum = 200;
//這個(gè)從來不這么用,但也算是正確的一種加鎖的機(jī)制:同步代碼塊。
//Object obj = new Object();
/**
* 存錢
* 要是不加[synchronized--同步函數(shù)],則會(huì)出現(xiàn)多線程安全問題。
*/
public synchronized void add(int n) {
//synchronized (obj) {
sum = sum + n;
try {
Thread.sleep(10);
}
catch (Exception ignore) {
}
//當(dāng)存錢次數(shù)變多的時(shí)候,就可以發(fā)現(xiàn),存錢的線程確實(shí)是2個(gè)在交替執(zhí)行存錢這個(gè)動(dòng)作的。
System.out.println(Thread.currentThread().getName() + "...sum=" + sum);
//}
}
/**
* 取錢
* 要是不加[synchronized--同步函數(shù)],則會(huì)出現(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ù)變多的時(shí)候,就可以發(fā)現(xiàn),存錢的線程確實(shí)是2個(gè)在交替執(zhí)行存錢這個(gè)動(dòng)作的。
System.out.println(Thread.currentThread().getName() + "...sum=" + sum);
}
}
在代碼里面有存和取2個(gè)方法,這2個(gè)方法,以及一個(gè)總金額,里面有部分被注釋掉的代碼,那個(gè)是簡(jiǎn)單易懂好理解的,多線程加鎖互斥,保證線程間同步的方法。
但是這個(gè)是不常用的方法,常用的就是使用synchronized這個(gè)關(guān)鍵字來修飾同步方法。
客戶對(duì)象的model
package com.lxk.threadTest.bank;
/**
* 顧客,實(shí)現(xiàn)runnable()接口,多個(gè)人可以一起存錢
*
* @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;
/**
* 對(duì)錢的操作類型,存錢or取錢
*/
private String type;
/**
* 操作的次數(shù),理論上是個(gè)正數(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);
}
}
}
}
客戶對(duì)象,因?yàn)榭梢院芏鄠€(gè)客戶同時(shí)訪問一個(gè)銀行,所以,這個(gè)存錢取錢的操作就用線程來實(shí)現(xiàn)。
屬性就構(gòu)造方法傳值了。
main方法
package com.lxk.threadTest.bank;
/**
* 銀行存錢的多線程實(shí)例
* <p>
* 【需求:】
* 銀行有一個(gè)金庫。
* 有兩個(gè)儲(chǔ)戶分別存或者取n * 100。
* 目的:該程序是否有安全問題,如果有,如何解決?
* <p>
* 【如何找問題:】
* 1,明確哪些代碼是多線程運(yùn)行代碼。
* 2,明確共享數(shù)據(jù)。
* 3,明確多線程運(yùn)行代碼中哪些語句是操作共享數(shù)據(jù)的。
*
* @author lxk on 2017/6/26
*/
public class Main {
public static void main(String[] args) {
//一個(gè)銀行and多個(gè)客戶
Bank bank = new Bank();
int time = 10000;
int money = 100;
//這個(gè)客戶存錢
Customer c1 = new Customer(bank, Customer.TYPE_ADD, time, money);
//這個(gè)客戶取錢
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í)際運(yùn)行效果如下圖。

這個(gè)存取錢的次數(shù)要是小了,就可能會(huì)看到2個(gè)線程有先后順序,所以,這個(gè)次數(shù)咱整多點(diǎn),然后,就看到如圖所示的情況,線程1是取錢的,線程0時(shí)存錢的,可以看到2個(gè)線程是互相交錯(cuò)執(zhí)行的,有存有取,沒有規(guī)律可言。
這個(gè)就保證了數(shù)據(jù)的同步了。
至于如何才能不同步,也就是異常的現(xiàn)象,
你可以把a(bǔ)dd方法的這個(gè)synchronized關(guān)鍵字去掉之后,把次數(shù)調(diào)小一點(diǎn)改成3次,sum的初始值給設(shè)置成0.你再試試代碼,
就會(huì)發(fā)現(xiàn)所謂的不同步現(xiàn)象。

上圖的右邊就是不同步的結(jié)果,2個(gè)人每次存100,存三次,總數(shù)是不是得,100,200,300,400,500,600.得長(zhǎng)。
但是,運(yùn)行結(jié)果卻不是的,
這個(gè)時(shí)候,你再把synchronized給add方法加上去,就會(huì)出現(xiàn)左邊的圖的結(jié)果,這個(gè)就是正確的結(jié)果。
我是為了,有存有取,所以,就又加了個(gè)方法。代碼就變成上面的樣子啦。
差不多都是線程間同步的例子啦。
我就簡(jiǎn)單記錄下代碼。用的時(shí)候,可以分分鐘就拿出來。
總結(jié)
以上就是本文關(guān)于以銀行取錢為例模擬Java多線程同步問題完整代碼的全部?jī)?nèi)容,希望對(duì)大家有所幫助。感興趣的朋友可以繼續(xù)參閱本站:
Java多線程定時(shí)器Timer原理及實(shí)現(xiàn)
如有不足之處,歡迎留言指出。感謝朋友們對(duì)本站的支持!
相關(guān)文章
Springboot項(xiàng)目通過redis實(shí)現(xiàn)接口的冪等性
這篇文章主要為大家介紹了Springboot項(xiàng)目通過redis實(shí)現(xiàn)接口的冪等性,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12
kafka運(yùn)維consumer-groups.sh消費(fèi)者組管理
這篇文章主要為大家介紹了kafka運(yùn)維consumer-groups.sh消費(fèi)者組管理,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11
PageHelper插件實(shí)現(xiàn)一對(duì)多查詢時(shí)的分頁問題
這篇文章主要介紹了PageHelper插件實(shí)現(xiàn)一對(duì)多查詢時(shí)的分頁問題,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
spring cloud如何修復(fù)zuul跨域配置異常的問題
最近的開發(fā)過程中,使用spring集成了spring-cloud-zuul,在配置zuul跨域的時(shí)候遇到了問題,下面這篇文章主要給大家介紹了關(guān)于spring cloud如何修復(fù)zuul跨域配置異常的問題,需要的朋友可以參考借鑒,下面來一起看看吧。2017-09-09
Java9新特性Stream流API優(yōu)化與增強(qiáng)
這篇文章主要為大家介紹了Java9新特性Stream流API優(yōu)化與增強(qiáng)的用法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助祝大家多多進(jìn)步,早日升職加薪2022-03-03
Java使用pdfbox實(shí)現(xiàn)給pdf文件加圖片水印
有時(shí)候需要給pdf加水印,市面上工具都是收費(fèi)的要會(huì)員,還是自食其力吧;嘗試過 spire.pdf.free 那個(gè)超過10頁就不行了!所以本文還是使用了pdfbox,感興趣的可以了解一下2022-11-11

