多线程安全系列之经典例子----实现多人对同一账户进行存取钱操作...

   日期:2020-07-05     浏览:94    评论:0    
核心提示:一、题目需求:现在有一个账户,这个账户的信息主要包括了账户号,账户名,账户余额。 那么请实现一下多人对这同一个账户进行存取钱的过程(保证线程安全的情况下)。二、需求分析1、根据题目要求,我们需要创建一个账户实体类(Account),并在里面创建一个实现存钱的方法和一个取钱的方法。package com.cover.day8;public class Account { private String ac

一、题目需求:

  • 现在有一个账户,这个账户的信息主要包括了账户号,账户名,账户余额。
  • 那么请实现一下多人对这同一个账户进行存取钱的过程(保证线程安全的情况下)。

二、需求分析

1、根据题目要求,我们需要创建一个账户实体类(Account),并在里面创建一个实现存钱的方法和一个取钱的方法。

package com.cover.day8;


public class Account {
	private String acccountCode;// 账户号码
	private String name;// 账户名
	private int balance;// 账户余额

	public String getAcccountCode() {
		return acccountCode;
	}

	public void setAcccountCode(String acccountCode) {
		this.acccountCode = acccountCode;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getBalance() {
		return balance;
	}

	public void setBalance(int balance) {
		this.balance = balance;
	}

	public Account() {
		super();
		// TODO Auto-generated constructor stub
	}

	public Account(String acccountCode, String name, int balance) {
		super();
		this.acccountCode = acccountCode;
		this.name = name;
		this.balance = balance;
	}

	// 存钱
	public synchronized void pos(int money) {
		String name = Thread.currentThread().getName();
		balance += money;
		System.out.println(name + "刚刚向账户" + getAcccountCode() + "中存入" + money + "元,当前余额为" + balance + "元");
		// 钱存好了,马上释放this这个锁了,这里可以叫醒其他线程,让他们从等待池进入到锁池
		// 这个锁一旦释放,那么就锁池中的线程就会进入到就绪状态,争夺执行权以及获取锁对象,然后再执行代码
		this.notifyAll();
	}

	// 取钱
	public synchronized void wit(int money) {
		String name = Thread.currentThread().getName();
		while (balance < money) {
			try {
				this.wait();// 等待存钱线程执行完以后执行,这样就避免了线程冲突,防止了死锁
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		balance -= money;
		System.out.println(name + "刚刚从账户" + getAcccountCode() + "中消费" + money + "元,当前余额为" + balance + "元");
	}
}

2、账户创建好后,那么我们就来创建用户线程调用相应的存取钱方法进行操作,为了好区分,我们可以定义一个女生用户进行取钱操作,定义一个男生用户来存钱。

package com.cover.day8;

public class GirlsThread extends Thread {
	private Account account; 
	
	public GirlsThread(Account account,String name) {
		super(name);
		this.account = account;
	}
	
	@Override
	public void run() {
		while(true) {
			//定义一个随机的钱的数量
			int money = (int)(Math.random()*1000+1);
			try {
				//调用取钱的方法进行随机取钱
				account.wit(money);
				//取完钱后睡眠会,让其他线程运行
				Thread.sleep(1000);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
}
package com.cover.day8;

public class BoyThread extends Thread {
	private Account account;

	public BoyThread(Account account,String name) {
		super(name);
		this.account = account;
	}

	@Override
	public void run() {
		while (true) {
               //生成随机钱的数量
			int money = (int) (Math.random() * 1000 + 1);
               //实现随机存钱
			account.pos(money);
			try {
                //当存完钱后进去休眠,然会让其他线程进行操作
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

}

3、这样,整体的需求就算是已经完成了,我们最后就写个测试类进行测试成功与否即可!

package com.cover.day8;

public class AccountTest {
	public static void main(String[] args) {
		//创建账户对象,并初始化属性值,账户号,账户名,账户余额(1000元)
		Account account = new Account("6220033004552316","tom",1000);
		//创建男生对象,用于实现存钱操作,操作人名为"tom"
		BoyThread boy = new BoyThread(account, "tom");
		//创建三个女生对象,用于实现取钱操作,操作人名分别为"lily1"、"lily2"、"lily3"
		GirlsThread girl1 = new GirlsThread(account, "lily1");
		GirlsThread girl2 = new GirlsThread(account, "lily2");
		GirlsThread girl3 = new GirlsThread(account, "lily3");
		//调用start方法启动相应的线程
		boy.start();
		girl1.start();
		girl2.start();
		girl3.start();
	}
}

4、这样我们的小练习就写好了,来看一下测试结果吧,为了方便操作,练习当中用的是一个死循环,所以必须手动停止,  不然会一直运行下去!

5、总结:在这个练习当中,为了保证线程安全,使用了synchronized关键字,也就是线程安全中锁的用法。那么对于锁的定义是怎么理解的呢,以下可以简单的阐述一下。

       在代码中,如果有多个线程,同时去访问一段相关的代码或者一个共享的数据,那么这时候就可能出现并发访问的问题。就如我们这个例子,这个账户就是属于共享的资源数据,多个用户去操作,就是属于并发访问,其中就会暗藏着线程安全问题。

       那么此时,synchronized就可以发挥作用了。synchronized可以对代码块进行加锁,锁的是这个代码中的代码,加锁之后,这个代码块中的代码只能让当前线程来执行,其他线程是不允许进来执行的,除非当前线程把这个代码块给执行完了或者把锁给释放了,那么其他线程才有机会进来执行。

       使用synchronized关键字修饰代码块之后,需要指定一个对象,来充当这把锁,然后把代码块中的代码给锁住。只有拿到这把锁的线程,才有权利去执行这个加锁的代码块。注意,这个时候,一个线程想去执行加锁的代码块,那么就需要俩个条件,第一个条件就是线程要抢夺到cpu时间片的执行权,第二个条件就是线程要拿到代码块上放置的这一把锁,这把锁就是允许进入到这个代码块的通行证。

       需要使用哪一个对象来充当这把锁,放置到加锁的代码块上面,作为可以进入到代码块中执行代码的通行证?
       可以使用java中的任何一个对象,来当做这把锁。但是如果想在多个线程执行代码的时候,达到线程同步的效果,  那么就需要让这多个线程先去争取synchronized关键字所指定的对象,而这个对象就是充当了这把锁。

        使用synchronized关键来修饰类中的【非静态方法】的时候,默认是使用this对象来当做这一把锁。

        使用synchronized关键来修饰类中的【静态方法】的时候,默认是使用当前类的Class对象来当做这一把锁。

        使用synchronized关键来修饰方法中的某一个代码块,那么这时候需要我们自己手动指定一个对象来当做这个一把锁。

 
打赏
 本文转载自:网络 
所有权利归属于原作者,如文章来源标示错误或侵犯了您的权利请联系微信13520258486
更多>最近资讯中心
更多>最新资讯中心
0相关评论

推荐图文
推荐资讯中心
点击排行
最新信息
新手指南
采购商服务
供应商服务
交易安全
关注我们
手机网站:
新浪微博:
微信关注:

13520258486

周一至周五 9:00-18:00
(其他时间联系在线客服)

24小时在线客服