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同步方法的的运行结果一样。

你可能感兴趣的:(java多线程)