首页 > 其他 > 详细

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

时间:2014-02-15 00:08:32      阅读:376      评论:0      收藏:0      [点我收藏+]

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

bubuko.com,布布扣
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();
            }
        }

    }
}
bubuko.com,布布扣

运行结果如下:

bubuko.com,布布扣

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

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

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

bubuko.com,布布扣
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();
            }
        }

    }
}
bubuko.com,布布扣

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

 bubuko.com,布布扣

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

bubuko.com,布布扣
/**
     * 用户取款
     * 
     * @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();
                }
            }
        }
    }
bubuko.com,布布扣

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

bubuko.com,布布扣
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();
        }
    }
bubuko.com,布布扣

 

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

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

原文:http://www.cnblogs.com/samzeng/p/3548457.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!