Java线程同步

来源:互联网 发布:php 直播 编辑:程序博客网 时间:2024/06/11 04:06

Java线程同步

使用synchronized来同步方法

一个模拟银行账户取款增款的例子

该例子共有4个类,分别是Account(账户类),Bank(取款),Company(增款),Client(用户)。Bank类会模拟100次取款,每次1000,Company类会模拟100次增款,每次1000。用户初始账户为1000,所以正确情况应该是余额依然为1000。

下面是Account源代码:

package com.zk;public class Account {    /** 余额 **/    private double balance;    /** 查询余额 **/    public double getBalance(){        return balance;    }    /** 设置余额 **/    public void setBalance(double balance){        this.balance = balance;    }    /** 增加余额 **/    public synchronized void addAmount(double amount){        double tmp = balance;        try{            Thread.sleep(10);        }catch(InterruptedException e){            e.printStackTrace();        }        tmp += amount;        balance = tmp;    }    /** 减少余额**/    public synchronized void substractAmount(double amount){        double tmp = amount;        try{            Thread.sleep(10);        }catch(InterruptedException e){            e.printStackTrace();        }        tmp -= amount;        balance = tmp;    }}

Bank代码:

package com.zk;/** 从银行中取款 **/public class Bank implements Runnable {    private Account account;    public Bank(Account account){        this.account = account;    }    @Override    public void run() {        for(int i=0; i<100; ++i){            account.substractAmount(1000);        }    }}

Company代码:

package com.zk;/** 公司会发工资 **/public class Company implements Runnable {    private Account account;    public Company(Account account){        this.account = account;    }    @Override    public void run() {        for(int i=0; i<100; ++i){            account.addAmount(1000);        }    }}

Client代码:

package com.zk;public class Client {    public static void main(String[] args) {        //初始账户:1000元        Account account = new Account();        account.setBalance(1000);        Company company = new Company(account);        Thread companyThread = new Thread(company);        Bank bank = new Bank(account);        Thread bankThread = new Thread(bank);        System.out.printf("Account: Initial Balance: %f.\n",                account.getBalance());        //启动两个线程:模拟100次存款(每次1000),100次取款(每次1000)        companyThread.start();        bankThread.start();        try {            //使用join方法来等待两个线程结束(die)            companyThread.join();            bankThread.join();            System.out.printf("Account: Final Balance: %f.\n.",                    account.getBalance());        } catch (InterruptedException e) {            e.printStackTrace();        }    }}

多次运行结果如下:

运行结果

通过Thread.sleep(10)我们来模拟取款增款时的一个延时。

在去掉addAmount()substractAmount()前面的关键字synchronized以后,当我们再次执行,会发现出现了错误的结果,而且每次结果不一样。这是因为JVM是不保证线程执行顺序,所以每次运行结果不一样。

而当我们添加上synchronized关键字的时候,这个时候当线程A正在执行Account类中标有synchronized方法的时候,如果有其他线程想同时执行synchronized方法,那么它将会阻塞直至线程A所正在执行的synchronized方法结束。同一时刻,只能有一个线程执行一个类中的一个synchronized方法。

在实际使用中,可以使用synchronized来锁一部分代码,而不是整个方法:

synchronized(this){    //...}

而且这样还可以根据需要来锁定不同的对象,而不是整个类:

private final Object cinemaA = new Object();private final Object cinemaB = new Object();/** 影院A销售票,锁定cinemaA对象即可 **/public void cinemaASellTickets(){    synchronized(cinemaA) {        //...    }}/** 影院B销售票,锁定cinemaB对象即可 **/public void clinemaBSellTickets(){    synchronized(cinemaB){        //...    }}

在synchronized代码块中使用条件

0 0