synchronized关键字详解
- 2019 年 10 月 30 日
- 笔记
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/luo4105/article/details/68069426
synchronized可以是线程的同步锁,可以修饰方法,也可以修饰代码块。作用是当多个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。但是多个并发线程访问多个对象调用这个锁住的方法时,同步锁不会产生作用。比如以下错误代码。
public class SyncTest { public static void main(String[] args) { Thread t1 = new Thread(){ //取钱线程 public void run(){ Bank b1 = new Bank(); try { b1.getMoney((double)50); } catch (Exception e) { System.out.println(e.getMessage()); e.printStackTrace(); } } }; Thread t2 = new Thread(){ //存钱线程 public void run(){ Bank b1 = new Bank(); try { b1.saveMoney((double)100); } catch (Exception e) { System.out.println(e.getMessage()); e.printStackTrace(); } } }; t2.start(); t1.start(); } } class Bank { private static Double money = new Double(0); /** * 取钱 * @param money * @throws Exception */ public synchronized void getMoney(Double m) throws Exception { if(Bank.money <= 0) { throw new Exception("错误提示1:卡上余额数据错误,当前余额为" + Bank.money + "元"); } if(Bank.money < money) { throw new Exception("错误提示1:卡上余额不足,当前余额为" + Bank.money + "元"); } else { Bank.money = Bank.money - money; System.out.println("成功取出" + money + "元,当前余额为" + Bank.money + "元"); } } /** * 存钱 * @param m * @throws Exception */ public synchronized void saveMoney(Double m) throws Exception { if(money < 0) { throw new Exception("卡上余额不足,当前余额为" + Bank.money + "元"); } else { Bank.money = money + m; System.out.println("成功存入" + m + "元,当前余额为" + Bank.money + "元"); } } }
虽然Bank类的getMoney()方法和saveMoney()方法都使用synchronized 关键字进行同步锁,但是在线程t1、t2中,同步的现象并没有发生,因为在t1、t2线程中重新给Bank实例化。是两个对象去执行同步锁方法,不是一个对象执行其同步锁方法,所以同步不会起效果。 正确的写法 1.两个线程调用同一个Bank对象。把Bank bank = new Bank()放在t1、t2实例化之前,t1、t2中的run方法都调用bank运行getMoney()和saveMoney()才能保证同步运行。 改正代码如下,在第3行、6行、16行对代码进行修改。
public static void main(String[] args) { Bank b1 = new Bank(); Thread t1 = new Thread(){ //取钱线程 public void run(){ try { b1.getMoney((double)50); } catch (Exception e) { System.out.println(e.getMessage()); e.printStackTrace(); } } }; Thread t2 = new Thread(){ //存钱线程 public void run(){ try { b1.saveMoney((double)100); } catch (Exception e) { System.out.println(e.getMessage()); e.printStackTrace(); } } }; t2.start(); t1.start(); }
2.两个线程调用两个Bank对象,但是Bank类中getMoney()方法和saveMoney()方法的同步锁锁的对象是private static Double money这个属性。即去掉getMoney()方法和saveMoney()方法的synchronized关键字,在方法内用synchronized(money){}将方法的语句块锁起来。这样,锁的是Bank.money这个对象,在getMoney()和saveMoney()方法同时调用Bank.money这个对象这个类对象时,同步锁就会起到作用。 改正代码如下,在第7行、27行去掉synchronized关键字修饰符,在第8、28行加上了synchronized(money){同步修饰代码段。
private static Double money = new Double(0); /** * 取钱 * @param money * @throws Exception */ public void getMoney(Double m) throws Exception { synchronized(money){ if(Bank.money <= 0) { throw new Exception("错误提示1:卡上余额数据错误,当前余额为" + Bank.money + "元"); } if(Bank.money < money) { throw new Exception("错误提示1:卡上余额不足,当前余额为" + Bank.money + "元"); } else { Bank.money = Bank.money - money; System.out.println("成功取出" + money + "元,当前余额为" + Bank.money + "元"); } } } /** * 存钱 * @param m * @throws Exception */ public void saveMoney(Double m) throws Exception { synchronized(money){ if(money < 0) { throw new Exception("卡上余额不足,当前余额为" + Bank.money + "元"); } else { Bank.money = money + m; System.out.println("成功存入" + m + "元,当前余额为" + Bank.money + "元"); } } }
3.将两个方法改为静态方法。这样,同步锁锁的是Bank这个类对象,在t1、t2线程中调用的也是同样的Bank类对象方法。这样也能达到同步的效果。 改正代码如下,在第7行、27行方法加上static修饰符。
private static Double money = new Double(0); /** * 取钱 * @param money * @throws Exception */ public synchronized static void getMoney(Double m) throws Exception { if(Bank.money <= 0) { throw new Exception("错误提示1:卡上余额数据错误,当前余额为" + Bank.money + "元"); } if(Bank.money < money) { throw new Exception("错误提示1:卡上余额不足,当前余额为" + Bank.money + "元"); } else { Bank.money = Bank.money - money; System.out.println("成功取出" + money + "元,当前余额为" + Bank.money + "元"); } } /** * 存钱 * @param m * @throws Exception */ public synchronized static void saveMoney(Double m) throws Exception { if(money < 0) { throw new Exception("卡上余额不足,当前余额为" + Bank.money + "元"); } else { Bank.money = money + m; System.out.println("成功存入" + m + "元,当前余额为" + Bank.money + "元"); } }
参考资料 [1].http://www.cnblogs.com/QQParadise/articles/5059824.html