Java多线程学习总结--线程同步(2)

线程同步是为了让多个线程在共享数据时,保持数据的一致性。举个例子,有两个人同时取钱,假设用户账户余额是1000,第一个用户取钱800,在第一个用户取钱的同时,第二个用户取钱600。银行规定,用户不允许透支,当余额不足时,应该取钱失败。我们先来看一下,如果线程不同步,会出现什么情况。代码如下:

public class SynchronizeApp {

    /**
     * @param args
     */
    public static void main(String[] args) {

        // 获得账户
        Account account = new Account();
        account.setCardNo("95559");
        account.setBalance(1000);
        // 用户1取款800
        DrawMoney user1 = new DrawMoney(account, 800);
        // 用户1取款600
        DrawMoney user2 = new DrawMoney(account, 600);
        user1.start();
        user2.start();
    }

}

class DrawMoney extends Thread {
    private Account account;
    private double amount;

    public DrawMoney(Account account, double amount) {
        this.account = account;
        this.amount = amount;
    }

    @Override
    public void run() {
        account.draw(amount);
    }
}

class Account {
    private String cardNo;
    private double balance;

    public String getCardNo() {
        return cardNo;
    }

    public void setCardNo(String cardNo) {
        this.cardNo = cardNo;
    }

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }

    /**
     * 用户取款
     * 
     * @param amount
     *            ,取款数量
     */
    public void draw(double amount) {

        if (amount > balance) {
            System.out.println("金额不足!");
        } else {
            try {
                // 模拟取款过程
                Thread.sleep(100);
                balance = balance - amount;
                System.out.println("成功取款" + amount + "元, 最新余额为" + balance);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}

运行结果如下:

可见,如果没有线程同步,当两个线程同时取款时,就会出现数据错误。第二个线程取款时,读取到的账户余额是1000,所以可以执行取款操作,但是进行实际取款时,账户余额被第一个线程修改,实际余额是200,所以取出600后最新余额是-400,同样第一个用户也出现了数据错误,余额是1000,取款800后却变成了-400。这种情况是不允许的。

线程同步有两种方式,第一种是用synchronized关键字,第二种是用lock对象。

使用synchronized关键字可以对方法和代码块进行同步,使用synchronized关键字对方法进行同步时,将synchronized关键字放在方法返回类型前面,synchronized自动锁定当前对象。上边取款操使用synchronized关键字同步方法的代码如下:

class Account {
    private String cardNo;
    private double balance;

    public String getCardNo() {
        return cardNo;
    }

    public void setCardNo(String cardNo) {
        this.cardNo = cardNo;
    }

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }

    /**
     * 用户取款
     * 
     * @param amount
     *            ,取款数量
     */
    public synchronized void draw(double amount) {

        if (amount > balance) {
            System.out.println("金额不足!");
        } else {
            try {
                // 模拟取款过程
                Thread.sleep(100);
                balance = balance - amount;
                System.out.println("成功取款" + amount + "元, 最新余额为" + balance);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}

再次运行程序,得到如下结果:

 

使用synchronized同步代码块的代码如下:

/**
     * 用户取款
     * 
     * @param amount
     *            ,取款数量
     */
    public void draw(double amount) {

        synchronized (this) {
            if (amount > balance) {
                System.out.println("金额不足!");
            } else {
                try {
                    // 模拟取款过程
                    Thread.sleep(100);
                    balance = balance - amount;
                    System.out.println("成功取款" + amount + "元, 最新余额为" + balance);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

还可以使用同步锁来对代码进行同步。使用同步锁时,先调用Lock对象的lock方法,代码执行完毕后,再调用Lock对象的unlock方法。在lock和unlock之间的代码是同步的,同一时间段内只能有一个线程能访问。为了保证能释放锁,把unlock方法放在finally语句块中是比较安全的,代码如下:

private final ReentrantLock lock = new ReentrantLock();

    /**
     * 用户取款
     * 
     * @param amount
     *            ,取款数量
     */
    public void draw(double amount) {
        lock.lock();
        try {
            if (amount > balance) {
                System.out.println("金额不足!");
            } else {
                // 模拟取款过程
                Thread.sleep(100);
                balance = balance - amount;
                System.out.println("成功取款" + amount + "元, 最新余额为" + balance);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

 

 运行代码,结果和使用synchronized同步方法的的运行结果一样。

转载于:https://www.cnblogs.com/samzeng/p/3548457.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值