Java线程四:线程同步

本文探讨了银行账户存款和取款操作中线程并发时可能出现的问题,如何通过使用synchronized关键字实现线程同步,确保账户余额的一致性。通过实例展示了如何在Bank类中使用synchronized来避免数据竞争导致的错误计算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

由前面几篇文章可知:线程什么时候运行是不确定的,即:

●各个线程是通过竞争CPU的时间而获得运行机会的;

●各线程什么时候获得CPU时间,占用多久,是不可预测的;

●一个正在运行的线程在什么地方被暂停是不确定的;

●可能一个线程中一个方法(或一个代码块)刚执行到一半,这个线程就丧失了CPU的使用权,从而导致很多问题;

上面几条都透露着几个字:不确定。这个问题,在实际开发中也会带来不可预测的结果,问题;

……………………………………………………

如下面问题:银行账户的存款和取款。

(1)首先,存款和取款各是不同的线程在执行;

(2)存款操作和取款操作都是分很多步骤执行;

(3)不同的线程之间切换是随机的,即可能取款线程执行一半就停了,转去执行存款线程,而存款线程执行一般也可能暂停,转去执行取款线程;这种现象就会导致,最终的账户金额存在错误计算;

如下面代码:看个大概即可,程序很简单,主要是传达一种问题而已。

(1)Bank类,主要包括,存款方法和取款方法;(这儿用slee()方法,防止输出结果输出太快,看不清;而且实际中,一个方法的执行也需要一定的时间的);

public class Bank {
	private String account;// 账号
	private int balance;// 账户余额

	public Bank(String account, int balance) {
		this.account = account;
		this.balance = balance;
	}

	public String getAccount() {
		return account;
	}

	public void setAccount(String account) {
		this.account = account;
	}

	public int getBalance() {
		return balance;
	}

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

	@Override
	public String toString() {
		return "Bank [账号:" + account + ", 余额:" + balance + "]";
	}

	// 存款
	public void saveAccount() {

		// 获取当前的账号余额
		int balance = getBalance();
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		// 修改余额,存100元
		balance += 100;
		// 修改账户余额
		setBalance(balance);
		// 输出存款后的账户余额
		System.out.println("存款后的账户余额为:" + balance);
	}

	public void drawAccount() {
			// 在不同的位置处添加sleep方法

			// 获得当前的帐户余额
			int balance = getBalance();
			// 修改余额,取200
			balance = balance - 200;
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			// 修改帐户余额
			setBalance(balance);
			System.out.println("取款后的帐户余额:" + balance);

	}
}

(2)取款类,做成了一个线程类(因为取款操作是一个单独的线程)

//取款
public class DrawAccount implements Runnable{
	Bank bank;
	public DrawAccount(Bank bank){
		this.bank=bank;
	}
	@Override
	public void run() {
		bank.drawAccount();
	}
	
}

(3)存款类,也做成了一个线程类

//存款
public class SaveAccount implements Runnable{
	Bank bank;
	public SaveAccount(Bank bank){
		this.bank=bank;
	}
	public void run(){
		bank.saveAccount();
	}
}

(4)测试类

public class Test {

	public static void main(String[] args) {
		// 创建帐户,给定余额为1000
		Bank bank=new Bank("1001",1000);
		//创建线程对象
		SaveAccount sa=new SaveAccount(bank);
		DrawAccount da=new DrawAccount(bank);
		Thread save=new Thread(sa);
		Thread draw=new Thread(da);
		save.start();
		draw.start();
		try {
			
			draw.join();
			save.join();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(bank);
	}

}

解决办法:

●为了保证在存款或取款的时候,不允许其他线程对账户余额进行操作;需要将Bank对象进行锁定,即某个线程在操作Bank对象时,其他线程都不允许进行操作;

●使用关键字synchronized(同步关键字)实现;该关键字,可以确保共享对象,在同一个时刻只能被一个线程访问,这种机制成为线程同步,或线程互斥。

synchroniced具体的使用技巧,需要在以后的实际开发中逐步加深理解,逐步总结其应用场景,惯用应用技巧等

synchroniced:

如上面Bank类改造如下:分别在存款方法和取款方法处,使用了synchroniced关键字。

如:saveAccount()方法添加synchronized后表示:这个方法中的所有代码在执行完毕之前,其他线程是不能把他打断的

public class Bank {
	private String account;// 账号
	private int balance;// 账户余额

	public Bank(String account, int balance) {
		this.account = account;
		this.balance = balance;
	}

	public String getAccount() {
		return account;
	}

	public void setAccount(String account) {
		this.account = account;
	}

	public int getBalance() {
		return balance;
	}

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

	@Override
	public String toString() {
		return "Bank [账号:" + account + ", 余额:" + balance + "]";
	}

	// 存款
	// saveAccount()方法添加synchronized后表示:这个方法中的所有代码在执行完毕之前,其他线程是不能把他打断的。
	public synchronized void saveAccount() {

		// 获取当前的账号余额
		int balance = getBalance();
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		// 修改余额,存100元
		balance += 100;
		// 修改账户余额
		setBalance(balance);
		// 输出存款后的账户余额
		System.out.println("存款后的账户余额为:" + balance);
	}

	public void drawAccount() {
		// 语句块添加synchronized后表示:这个方法中的所有代码在执行完毕之前,其他线程是不能把他打断的。
		// 因为当前语句块是针对Bank类的,即是针对Bank类型的对象的,所以直接this就行了
		synchronized (this) {
			// 在不同的位置处添加sleep方法

			// 获得当前的帐户余额
			int balance = getBalance();
			// 修改余额,取200
			balance = balance - 200;
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			// 修改帐户余额
			setBalance(balance);
			System.out.println("取款后的帐户余额:" + balance);
		}

	}
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值