使用synchronized实现同步方法

在开始本篇前我们先来看个例子,大家可以自己先看看代码想想运行后的结果。

例子说明:

1、在本例中将模拟两个线程(公司、家庭)同时操作账户,公司负责往账户中存钱,家庭负责花钱,循环100次。

2、存钱和花钱时都先取出账户中的钱放入临时变量,然后再将当前线程休眠固定时间,最后将用临时变量的值加减金额后重新赋值给账户。

3、初始账户中有人民币1000元。


package com.sqczm.concurrent.sync;

import java.util.concurrent.TimeUnit;

/**
 * <p>Description: 账户 </p>
 * <p>Copyright: Copyright (c) 2014 </p>
 * <p>Date: 2014年11月4日 </p>
 * <p>Company: Ausware </p>
 * @author ygczm
 * @version V1.0
 */
public class Account {
	private double money;
	
	/**
	 * 账户资金
	 * @return 账户资金
	 */
	public double getMoney() {
		return money;
	}
	
	/**
	 * 账户资金
	 * @param money 账户资金
	 */
	public void setMoney(double money) {
		this.money = money;
	}
	
	/**
	 * 存钱
	 * @param num 数量
	 */
	public synchronized void addMoney(double num){
		System.out.println("-->开始存钱......");
		double temp = money;
		try {
			TimeUnit.MILLISECONDS.sleep(21l);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		money = temp + num;
		System.out.println("-->存钱结束......");
	}
	
	/**
	 * 取钱
	 * @param num 数量
	 */
	public synchronized void subMoney(double num){
		System.out.println("-->开始取钱......");
		double temp = money;
		try {
			TimeUnit.MILLISECONDS.sleep(19l);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		money = temp - num;
		System.out.println("-->取钱结束......");
	}
}


package com.sqczm.concurrent.sync;
/**
 * <p>Description: 家庭 </p>
 * <p>Copyright: Copyright (c) 2014 </p>
 * <p>Date: 2014年11月4日 </p>
 * <p>Company: Ausware </p>
 * @author ygczm
 * @version V1.0
 */
public class Family implements Runnable {
	private Account account;
	
	public Family(Account account) {
		this.account = account;
	}
	
	@Override
	public void run() {
		for(int i = 0; i < 100; i++){
			account.subMoney(1000);
		}
	}
	
}

package com.sqczm.concurrent.sync;
/**
 * <p>Description: 公司 </p>
 * <p>Copyright: Copyright (c) 2014 </p>
 * <p>Date: 2014年11月4日 </p>
 * <p>Company: Ausware </p>
 * @author ygczm
 * @version V1.0
 */
public class Company implements Runnable {
	private Account account;
	
	public Company(Account account) {
		this.account = account;
	}
	
	@Override
	public void run() {
		for(int i = 0; i < 100; i++){
			account.addMoney(1000);
		}
	}

}


package com.sqczm.concurrent.sync;
/**
 * <p>Description: synchronized的测试类 </p>
 * <p>Copyright: Copyright (c) 2014 </p>
 * <p>Date: 2014年11月4日 </p>
 * <p>Company: Ausware </p>
 * @author ygczm
 * @version V1.0
 */
public class SynchronizedTest {
	public static void main(String[] args) {
		//模拟创建一个账户
		Account account = new Account();
		account.setMoney(1000);
		
		//模拟创建两个线程(公司存钱、家庭花钱)
		Company company = new Company(account);
		Family family = new Family(account);
		Thread companyThread = new Thread(company);
		Thread familyThread = new Thread(family);
		
		//在启动线程前先打印账户信息
		System.out.println("-->账户中初始余额为 " + account.getMoney());
		
		//启动两个线程
		companyThread.start();
		familyThread.start();
		
		//等待两个线程运行完成
		try {
			companyThread.join();
			familyThread.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		//线程运行完毕后打印账户信息
		System.out.println("-->账户中还剩 " + account.getMoney());
	}
}


    看完上面的例子后相信大家会有两种想法:一种就是账户中最后还剩1000元,另一种就是账户中剩余的钱不确定。咱们先不讨论上面的例子运行后的结果是多少,咱们先看看synchronized同步方法的说明:每个类实例对应一把锁,每个 synchronized 方法都必须获得调用该方法的类实例的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。这种机制确保了同一时刻对于每一个类实例,其所有声明为 synchronized 的成员函数中至多只有一个处于可执行状态(因为至多只有一个能够获得该类实例对应的锁),从而有效避免了类成员变量的访问冲突(只要所有可能访问类成员变量的方法均被声明为synchronized)。 

    看完了上面的解释后,我们应该都猜到了,在同一时间内不可能同时操作取钱和存钱的操作,也就是说在上面存钱和取钱的两个操作中,共享数据(账户金额)得到了保护,最终我们的账户金额不会受影响。

    synchronized关键字会降低应用程序的性能,因此只能在并发情景中需要修改共享数据的方法上使用它。如果多个线程访问同一个synchronized方法,则只有一个线程可以访问,其他线程将等待。如果方法申明中没有使用synchronized关键字,所有的线程都能在同一时间执行这个方法(可将上面的Account类中的存钱或取钱的方法前的synchronized关键字去掉后测试),因而总运行时间将降低。如果已知一个方法不会被一个以上线程调用,则无需使用synchronized关键字申明之。

    可以递归调用被synchronized申明的方法。当线程访问一个对象的同步方法时,它还可以调用这个方法对象的其他的同步方法,也包含正在执行的方法,而不必再去获取这个方法的访问权。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值