模拟银行取存款
==================线程不安全的===============================================
1.先弄一个银行账户对象,封装了存取插钱的方法:
package com.test.threadDemo2;
/**
* 银行账户
* @author Administrator
*
*/
public class Acount {
private int count=0;
/**
* 存钱
* @param money
*/
public void addAcount(String name,int money) {
17 // 存钱
count += money;
System.out.println(name+"...存入:"+money+"..."+Thread.currentThread().getName());
SelectAcount(name);
22 }
/**
* 取钱
* @param money
*/
public void subAcount(String name,int money) {
30 // 先判断账户现在的余额是否够取钱金额
if(count-money < 0){
System.out.println("账户余额不足!");
return;
}
// 取钱
count -= money;
System.out.println(name+"...取出:"+money+"..."+Thread.currentThread().getName());
SelectAcount(name);
40 }
/**
* 查询余额
*/
public void SelectAcount(String name) {
System.out.println(name+"...余额:"+count);
}
public static void main(String[] args) {
// 开个银行帐号
Account account = new Account();
// 开银行帐号之后银行给张银行卡
Card card = new Card("card",account);
// 开银行帐号之后银行给张存折
Paper paper = new Paper("存折",account);
Thread thread1 = new Thread(card);
Thread thread2 = new Thread(paper);
thread1.start();
thread2.start();
}
}
2.编写银行卡对象
package com.test.threadDemo2;
/**
* 银行卡负责存钱
* @author Administrator
*
*/
public class Card implements Runnable{
private String name;
private Account account = new Account();
public Card(String name,Account account) {
this.account = account;
this.name = name;
}
@Override
public void run() {
while(true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
account.addAccount(name,100);26 }
}
}
3.编写存折对象
package com.test.threadDemo2;
/**
* 存折负责取钱
* @author Administrator
*
*/
public class Paper implements Runnable{
private String name;
private Account account = new Account();
public Paper(String name,Account account) {
this.account = account;
this.name = name;
}
@Override
public void run() {
while(true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
account.subAccount(name,50);
}
}
}
由于对存取钱的账户没有锁定,导致线程不安全的,会产生并发问题。造成,账户余额为负。
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/75d34b192df851a24fd498af33203e7a.png)
解决措施
==============================================以下是线程安全的===================
同步代码块
package com.test.threadDemo2;
/**
* 银行账户
* @author Administrator
*
*/
public class Acount {
private int count=0;
/**
* 存钱
* @param money
*/
public void addAcount(String name,int money) {
synchronized(this) {
// 存钱
count += money;
System.out.println(name+"...存入:"+money+"..."+Thread.currentThread().getName());
SelectAcount(name);
}
}
/**
* 取钱
* @param money
*/
public void subAcount(String name,int money) {
synchronized(this) {
// 先判断账户现在的余额是否够取钱金额
if(count-money < 0){
System.out.println("账户余额不足!");
return;
}
// 取钱
count -= money;
System.out.println(name+"...取出:"+money+"..."+Thread.currentThread().getName());
SelectAcount(name);
}
}
/**
* 查询余额
*/
public void SelectAcount(String name) {
System.out.println(name+"...余额:"+count);
}
}
同步方法块
package com.test.threadDemo2;
/**
* 银行账户
* @author Administrator
*
*/
public class Acount {
private int count;
/**
* 存钱
* @param money
*/
public synchronized void addAcount(String name,int money) {
// 存钱
count += money;
System.out.println(name+"...存入:"+money);
}
/**
* 取钱
* @param money
*/
public synchronized void subAcount(String name,int money) {
// 先判断账户现在的余额是否够取钱金额
if(count-money < 0){
System.out.println("账户余额不足!");
return;
}
// 取钱
count -= money;
System.out.println(name+"...取出:"+money);
}
/**
* 查询余额
*/
public void SelectAcount(String name) {
System.out.println(name+"...余额:"+count);
}
}
3 使用同步锁:
account 类创建私有的 ReetrantLock 对象,调用 lock() 方法,同步执行体执行完毕之后,需要用 unlock() 释放锁。
package com.test.threadDemo2;
import java.util.concurrent.locks.ReentrantLock;
/**
* 银行账户
* @author Administrator
*
*/
public class Acount {
private int count;
private ReentrantLock lock = new ReentrantLock();
/**
* 存钱
* @param money
*/
public void addAcount(String name,int money) {
lock.lock();
try{
// 存钱
count += money;
System.out.println(name+"...存入:"+money);
}finally {
lock.unlock();
}
}
/**
* 取钱
* @param money
*/
public void subAcount(String name,int money) {
lock.lock();
try{
// 先判断账户现在的余额是否够取钱金额
if(count-money < 0){
System.out.println("账户余额不足!");
return;
}
// 取钱
count -= money;
System.out.println(name+"...取出:"+money);
}finally {
lock.unlock();
}
}
/**
* 查询余额
*/
public void SelectAcount(String name) {
System.out.println(name+"...余额:"+count);
}
}
二、死锁
当线程需要同时持有多个锁时,有可能产生死锁。考虑如下情形:
线程 A 当前持有互斥所锁 lock1,线程 B 当前持有互斥锁 lock2。
接下来,当线程 A 仍然持有 lock1 时,它试图获取 lock2,因为线程 B 正持有 lock2,因此线程 A 会阻塞等待线程 B 对 lock2 的释放。
如果此时线程 B 在持有 lock2 的时候,也在试图获取 lock1,因为线程 A 正持有 lock1,因此线程 B 会阻塞等待 A 对 lock1 的释放。
二者都在等待对方所持有锁的释放,而二者却又都没释放自己所持有的锁,这时二者便会一直阻塞下去。这种情形称为死锁。