java 多线程 4 : 同步代码块

线程安全问题:多线程访问同一数据,容易出现线程安全问题

经典问题:银行取钱,1.用户名密码登陆,2.输入取款金额,3.判断账户余额是否大于取款金额,4.如果大于,取款成功,如果小于,取款失败。

public class Account {
    private String account;//账号
    private double balance;//余额
    
    public Account() {
        super();
    }
    public Account(String account, double balance) {
        super();
        this.account = account;
        this.balance = balance;
    }
    public String getAccount() {
        return account;
    }
    public void setAccount(String account) {
        this.account = account;
    }
    public double getBalance() {
        return balance;
    }
    public void setBalance(double balance) {
        this.balance = balance;
    }
}
public class DrawThread extends Thread{
    private Account account;//模拟账户
    private double drawMoney;//当前取钱线程想要取的钱数
    public DrawThread(String name , Account account, double drawMoney) {
        super(name);
        this.account = account;
        this.drawMoney = drawMoney;
    }
    
    public void run() {
        if(account.getBalance() >= drawMoney) {
            System.out.println(getName() + "取钱成功!吐出钞票:" + drawMoney);
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            account.setBalance(account.getBalance() - drawMoney);
            System.out.println("\t余额为:" + account.getBalance());
        }else {
            System.out.println(getName() + "取钱失败!余额不足");
        }
    }
    
    public static void main(String[] args) {
        Account account = new Account("123456" , 1000);
        new DrawThread("A", account, 800).start();
        new DrawThread("B", account, 800).start();
    }
}

 

同步代码块:上面Demo中,两个并发线程在修改Account对象,很容易造成异常;为了解决这样的异常,java支持引入了同步监视器来解决这个问题,使用同步监视器的通用方法就是同步代码块,同步代码块的语法:

synchronized(Obj){
  ...//同步代码块
}

任何时刻只能有一个线程可以获得对同步监视器的锁定,当同步代码块执行完成之后,该线程会释放对该同步监视器的锁定。
虽然java程序允许任何对象作为同步监视器,但是同步监视器的目的:是为了阻止两个线程对一个共享资源进行并发访问,因而推荐使用可能被并发访问的共享资源充当同步监视器

public void run() {
        //使用account作为同步监视器,任何线程进入同步代码块之前必须获得对account的锁定,其他线程无法解锁,也无法修改它
        //加锁 --> 执行代码块 -->释放锁
        synchronized(account) {
            if(account.getBalance() >= drawMoney) {
                System.out.println(getName() + "取钱成功!吐出钞票:" + drawMoney);
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                account.setBalance(account.getBalance() - drawMoney);
                System.out.println("\t余额为:" + account.getBalance());
            }else {
                System.out.println(getName() + "取钱失败!余额不足");
            }
        }
    }

 

posted @ 2018-09-19 15:24  new_boys  阅读(198)  评论(0编辑  收藏  举报