java 并发编程(二)之synchronized实例


“java 并发编程(六)之synchronized”介绍了java控制“临界区”访问的第一种方法“synchronized”,上节讲到synchronized方式主要注意这么几点

1、临界区的概念;

2、临界区的重入;

3、synchronized修饰静态方法的特殊性,如果静态方法也修改了“临界区”的公共资源,那么要注意,可能会出现并发问题;

这里我举出了个synchronized的使用实例:

模拟一个银行从账户取出一定金额,而公司存入账户金额这么一个过程。

用到了四个类

账户:Account

package com.z;

import java.util.concurrent.TimeUnit;

public class Account {
	
	private int account;
	
	public void setAccount(int newAccount){
		this.account = newAccount;
	}
	
	public int getAccount(){
		return account;
	}
	
	public synchronized boolean addAccount(int newAccount){
		int tmp = account;
		try {
			TimeUnit.MICROSECONDS.sleep(30);
		} catch (InterruptedException e) {
			System.err.println("账户添加失败:" + e);
			return false;
		}
		tmp += newAccount;
		account = tmp;
		return true;
	}
	
	public synchronized boolean subAccount(int newAccount){
		int tmp = account;
		try{
			TimeUnit.MICROSECONDS.sleep(30);
		} catch(InterruptedException e){
			System.err.println("账户扣除失败:" + e);
			return false;
		}
		tmp -= newAccount;
		account = tmp;
		return true;
	}

}

银行类:

package com.z;

public class Bank implements Runnable{
	
	private final Account account;
	
	public Bank(Account account){
		this.account = account;
	}

	public void run(){
		for(int i = 0; i < 10; i ++){
			account.subAccount(1000);
		}
	}
	
}


公司类:

package com.z;

public class Company implements Runnable{
	
	private final Account account;
	
	public Company(Account account){
		this.account = account;
	}

	public void run(){
		for(int i = 0; i < 10; i ++){
			account.addAccount(1000);
		}
	}
	
}

测试类:

package com.z;

public class Test {

	public static void main(String[] args){
		Account account = new Account();
		account.setAccount(1000);
		Thread[] bankThread = new Thread[100];
		Thread[] companyThread = new Thread[100];
		for(int i = 0; i < 100; i ++){
			bankThread[i] = new Thread(new Bank(account));
			companyThread[i] = new Thread(new Company(account));
		}
		for(int i = 0; i < 100; i ++){
			bankThread[i].start();
			companyThread[i].start();
		}
		for(int i = 0; i < 100; i ++){
			try {
				bankThread[i].join();
				companyThread[i].join();
			} catch (InterruptedException e) {
				System.err.println(e);
			}
		}
		
		System.out.println("账户余额是:" + account.getAccount());
	}
	
}

通过反复多次执行输出的结果是:

账户余额是:1000

跟我们的预想是符合的。


在这里要注意两点:

1、如果我们把Account类中的addAccount和subAccount的synchronized关键字去掉,就会看到输出我们预想到的千奇百怪的结果;

2、我们观察addAccount和subAccount方法会发现,我们刻意写成这样的形式:

<span style="white-space:pre">		</span>int tmp = account;
		try {
			TimeUnit.MICROSECONDS.sleep(30);
		} catch (InterruptedException e) {
			System.err.println("账户添加失败:" + e);
			return false;
		}
		tmp += newAccount;
		account = tmp;
		return true;
<pre name="code" class="java"><span style="white-space:pre">		</span>int tmp = account;
		try {
			TimeUnit.MICROSECONDS.sleep(30);
		} catch (InterruptedException e) {
			System.err.println("账户添加失败:" + e);
			return false;
		}
		tmp -= newAccount;
		account = tmp;
		return true;

 

我们知道java会把类转化为字节码的形式,这正是java灵活性的根源,因此即使是最简单的赋值语句a=b,我们在观察生成的字节码的时候也会发现

是转化为了多条指令语句,因此即使是简单的a=b也会产生并发问题。所以,在这里我们用了个临时变量tmp来作为中转来模拟这个过程,这样会更容易产生

预想的问题。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值