多线程synchronized使用

来源:互联网 发布:阿玛迪斯战记 数据 编辑:程序博客网 时间:2024/06/06 20:32

多线程synchronized使用

概述

synchronized 关键字,代表这个方法加锁,相当于不管哪一个线程(例如线程A),运行到这个方法时,都要检查有没有其它线程B(或者C、 D等)正在用这个方法(或者该类的其他同步方法),有的话要等正在使用synchronized方法的线程B(或者C 、D)运行完这个方法后再运行此线程A,没有的话,锁定调用者,然后直接运行。(摘自百度百科:https://baike.baidu.com/item/synchronized/8483356?fr=aladdin)

案例分析

  • 需求说明:假设你的银行账户余额10000元。某日,你通过网上商城支付商品费用扣除费用100元,同时,你的朋友还款将向你的账户充值100元。这里涉及到一个多线程的问题,正常情况下,你的账户余额应该是10000元。
    可能会出现的异常是:
    1. 你向商家支付账款时,银行系统取出你的账户余额10000元,同时,你的朋友向你的账户发送了充值的请求,此时,向商家支付账款的请求还未结束,账户余额仍是10000元,所以,这时出现的结果是你的账户余额是10100元。
    2. 反过来讲,你的朋友先向你的账户发起了转账的申请,同时你也向商家账户发起了支付账款的请求,这时出现的情况可能是,账户余额在10000元的基础上先做了累加操作,同时又做了递减操作,那么你的余额便成9900元。
      那么,如何解决这种情况呢?这就需要用到线程锁,即synchronized.

代码示例

  • 创建账户余额类,并为结算方法做线程锁操作。
public class Account {    private int balance = 10000; // 账户余额    public int getBalance() {        return balance;    }    public void setBalance(int balance) {        this.balance = balance;    }    /**     * 账户余额结算方法     * @param operationType 操作类型     * @param operationMoney 操作金额     * @return     */    public synchronized int calc(String operationType, int operationMoney) {        switch (operationType) {        case "add": // 添加操作            this.balance += operationMoney;            break;        case "reduce": // 减少操作            this.balance -= operationMoney;            break;        default:            break;        }        return this.balance;    }}
  • 创建测试类
public class SynchronizedDemo {    public static void main(String[] args) {        Account account = new Account();        System.out.println("账户初始余额:" + account.getBalance());        for (int i = 0; i < 5; i++) {            new Thread(new Runnable() {                @Override                public void run() {                    int balance = account.calc("add", 100);                    System.out.println("当前线程:" + Thread.currentThread().getName() + "\t账户余额-添加操作:" + balance);                }            }, "addBalance-thread").start();            new Thread(new Runnable() {                @Override                public void run() {                    int balance = account.calc("reduce", 100);                    System.out.println("当前线程:" + Thread.currentThread().getName() + "\t账户余额-减少操作:" + balance);                }            }, "reduceBalance-thread").start();        }    }}

在结算方法calc()未加synchronized线程锁的时候,输出结果如下:

账户初始余额:10000当前线程:addBalance-thread  账户余额-添加操作:10100当前线程:reduceBalance-thread   账户余额-减少操作:10000当前线程:reduceBalance-thread   账户余额-减少操作:9900当前线程:addBalance-thread  账户余额-添加操作:10100当前线程:addBalance-thread  账户余额-添加操作:10000当前线程:addBalance-thread  账户余额-添加操作:10200当前线程:reduceBalance-thread   账户余额-减少操作:10100当前线程:reduceBalance-thread   账户余额-减少操作:10000当前线程:reduceBalance-thread   账户余额-减少操作:9900当前线程:addBalance-thread  账户余额-添加操作:10000

观察日志,发现在第三次和第四次操作时就出现了错误。账户余额多了100.
上述结果需要多运行几次,每次的结果不一致,但是会出现类似的错误的。
那么在calc()加上线程锁后,运行日志如下:
第一次运行结果:

账户初始余额:10000当前线程:addBalance-thread  账户余额-添加操作:10100当前线程:addBalance-thread  账户余额-添加操作:10200当前线程:addBalance-thread  账户余额-添加操作:10300当前线程:reduceBalance-thread   账户余额-减少操作:10200当前线程:addBalance-thread  账户余额-添加操作:10300当前线程:addBalance-thread  账户余额-添加操作:10400当前线程:reduceBalance-thread   账户余额-减少操作:10300当前线程:reduceBalance-thread   账户余额-减少操作:10200当前线程:reduceBalance-thread   账户余额-减少操作:10100当前线程:reduceBalance-thread   账户余额-减少操作:10000

第二次运行结果:

账户初始余额:10000当前线程:addBalance-thread  账户余额-添加操作:10100当前线程:reduceBalance-thread   账户余额-减少操作:10000当前线程:addBalance-thread  账户余额-添加操作:10100当前线程:reduceBalance-thread   账户余额-减少操作:10000当前线程:addBalance-thread  账户余额-添加操作:10100当前线程:reduceBalance-thread   账户余额-减少操作:10000当前线程:addBalance-thread  账户余额-添加操作:10100当前线程:reduceBalance-thread   账户余额-减少操作:10000当前线程:addBalance-thread  账户余额-添加操作:10100当前线程:reduceBalance-thread   账户余额-减少操作:10000

多次测试是没有问题的。
所以synchronized线程锁解决了这种多线程问题。

源代码:https://github.com/myNameIssls/javase-study/tree/master/javase-multithreading

原创粉丝点击