同步方法同步块Synchronized

63 篇文章 0 订阅
60 篇文章 0 订阅
本文通过一个银行取款的例子,探讨了Java中线程安全问题。当多个线程同时操作账户余额时,未进行同步控制会导致数据不一致。分析了在`run()`方法上加`synchronized`关键字以及使用同步块的两种尝试,并指出只有当同步块锁定在实际共享资源(如`Account`对象)上时,才能确保线程安全。
摘要由CSDN通过智能技术生成

同步方法同步块Synchronized

synchronized可以加在方法上,使方法变成同步方法。也可以当成同步块使用。

public class UnSafeBank {
    public static void main(String[] args) {
        //创建一个账户
        Account account = new Account("蓝色的番茄", 10000);
        //创建两个取钱线程
        Drawing you = new Drawing(account, 5000, "你");
        Drawing gilrFriend = new Drawing(account, 8000, "女朋友");
        //取钱
        you.start();
        gilrFriend.start();

    }
}

//账户
class Account {
    private String name;
    private int salaryMoney;

    public Account(String name, int salaryMoney) {
        this.name = name;
        this.salaryMoney = salaryMoney;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getSalaryMoney() {
        return salaryMoney;
    }

    public void setSalaryMoney(int salaryMoney) {
        this.salaryMoney = salaryMoney;
    }
}

//银行取钱
class Drawing extends Thread {
    //账户
    private Account account;
    //取多少钱
    private int drawingMoney;


    public Drawing(Account account, int drawingMoney, String name) {
        //调用父类方法设置名字
        super(name);
        this.account = account;
        this.drawingMoney = drawingMoney;
    }

    @Override
    public void run() {
        //判断能否取钱
        if (account.getSalaryMoney() - drawingMoney < 0) {
            System.out.println(Thread.currentThread().getName() + "取钱" + drawingMoney + "余额不足");
            return;
        }
        //模拟取钱时间
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //设置账户余额
        account.setSalaryMoney(account.getSalaryMoney() - drawingMoney);

        //取钱成功,并打印
        System.out.println("卡里剩余余额" + account.getSalaryMoney());
        System.out.println(Thread.currentThread().getName() + "取到了" + drawingMoney);

    }
}

上述代码中Account是一个账户类,包含账户名和余额。Drawing是银行取钱,包含账户和取多少钱。账户里有10000元,你取5000,女朋友取8000。执行代码就会出现线程不安全的情况。

在run方法上加Synchronized修饰符
@Override
    public synchronized void run() {
            //判断能否取钱
            if (account.getSalaryMoney() - drawingMoney < 0) {
                System.out.println(Thread.currentThread().getName() + "取钱" + drawingMoney + "余额不足");
                return;
            }
            //模拟取钱时间
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //设置账户余额
            account.setSalaryMoney(account.getSalaryMoney() - drawingMoney);

            //取钱成功,并打印
            System.out.println("卡里剩余余额" + account.getSalaryMoney());
            System.out.println(Thread.currentThread().getName() + "取到了" + drawingMoney);
    }

这里的run方法是一个普通方法,使用Synchronized之后锁的是this对象,两个this分别是you和gilrFriend。因为它们上锁的不是同一个对象,所以加了Synchronized任然无法达到线程安全。

使用同步块,里面放this对象
synchronized (this) {
            //判断能否取钱
            if (account.getSalaryMoney() - drawingMoney < 0) {
                System.out.println(Thread.currentThread().getName() + "取钱" + drawingMoney + "余额不足");
                return;
            }
            //模拟取钱时间
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //设置账户余额
            account.setSalaryMoney(account.getSalaryMoney() - drawingMoney);

            //取钱成功,并打印
            System.out.println("卡里剩余余额" + account.getSalaryMoney());
            System.out.println(Thread.currentThread().getName() + "取到了" + drawingMoney);
        }

使用这种方法依然不能保证线程安全,因为两个this是指方法的调用者,即you和gilrFriend,锁的是两个不同的对象。

使用同步块,里面放具体对象
synchronized (account) {
            //判断能否取钱
            if (account.getSalaryMoney() - drawingMoney < 0) {
                System.out.println(Thread.currentThread().getName() + "取钱" + drawingMoney + "余额不足");
                return;
            }
            //模拟取钱时间
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //设置账户余额
            account.setSalaryMoney(account.getSalaryMoney() - drawingMoney);

            //取钱成功,并打印
            System.out.println("卡里剩余余额" + account.getSalaryMoney());
            System.out.println(Thread.currentThread().getName() + "取到了" + drawingMoney);
        }

这里you和gilrFriend对象里的account是同一个对象,因此上锁之后另一个线程就无法进入。保证了线程的安全性。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值