使用synchronized实现同步方法

来源:互联网 发布:数据机房管理制度 编辑:程序博客网 时间:2024/05/16 07:33

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

例子说明:

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;}@Overridepublic 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;}@Overridepublic 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
原创粉丝点击