java线程:线程同步

这篇博客探讨了Java中多线程环境下银行存取款操作可能导致的问题,通过示例代码展示了在无同步机制时出现的并发异常。通过引入`synchronized`关键字实现线程同步,确保了在同一时刻只有一个线程能访问共享资源,避免了数据不一致的情况。这强调了在并发编程中同步控制的重要性。
摘要由CSDN通过智能技术生成

多线程运行问题

  • 各个线程是通过竞争CPU时间而获得运行机会的
  • 各线程什么时候得到CPU时间,占用多久,是不可预测的一个正在运行着
  • 线程在什么地方被暂停是不确定的

编写银行存、取款代码:

package java_thread;

public class BankThread {
    private String account;  //账号
    private int balance;  //账户余额
    public BankThread(String account, int balance){
        this.account = account;
        this.balance = balance;
    }
    public String getAccount() {
        return account;
    }
    public void setAccount(String account) {
        this.account = account;
    }
    public int getBalance() {
        return balance;
    }
    public void setBalance(int balance) {
        this.balance = balance;
    }
    @Override
    public String toString(){
        return "Bank [账号:" + account + ", 余额:" + balance + "]";
    }
    
    //存款
    public void saveAccount(){
        //获取当前的账号余额
        int balance = getBalance();
        //修改余额,存100
        balance += 100;
        //修改账户余额
        setBalance(balance);
        //输出存款后的账户余额
        System.out.println("存款后的账户余额为:" + balance);
    }

    //取款
    public void drawAccount(){
        //获得当前的账户余额
        int balance = getBalance();
        //修改余额,取200
        balance = balance - 200;
        //修改账户余额
        setBalance(balance);
        System.out.println("取款后的账户余额:" + balance);
    }
}

存款类,实现Runnable接口:

package java_thread;

//存款
public class SaveAccount implements Runnable {
    BankThread bank;
    public SaveAccount(BankThread bank){
        this.bank = bank;
    }

    @Override
    public void run() {
        bank.saveAccount();
    }
}

取款类,实现Runnable接口:

package java_thread;

//取款
public class DrawAccount implements Runnable {
    BankThread bank;
    public DrawAccount(BankThread bank){
        this.bank = bank;
    }
    
    @Override
    public void run() {
        bank.drawAccount();
    }
}

进行测试:

package java_thread;

public class BankTest {
    public static void main(String[] args) {
        //创建账户,给定余额为1000
        BankThread bank = new BankThread("s01", 1000);
        //创建线程对象
        SaveAccount save = new SaveAccount(bank);
        DrawAccount draw = new DrawAccount(bank);
        Thread sa = new Thread(save);
        Thread dr = new Thread(draw);
        sa.start();
        dr.start();
        try {
            dr.join();
            sa.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(bank);
    }
}

输出:

存款后的账户余额为:1100
取款后的账户余额:900
Bank [账号:s01, 余额:900]

结果看似好像没有什么问题。
若在存取款方法中添加休眠状态:

//存款
public void saveAccount(){
    //获取当前的账号余额
    int balance = getBalance();
    
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    //修改余额,存100
    balance += 100;
    //修改账户余额
    setBalance(balance);
    //输出存款后的账户余额
    System.out.println("存款后的账户余额为:" + balance);
}

//取款
public void drawAccount(){
    //获得当前的账户余额
    int balance = getBalance();
    //修改余额,取200
    balance = balance - 200;

    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    //修改账户余额
    setBalance(balance);
    System.out.println("取款后的账户余额:" + balance);
}

再次输出:

存款后的账户余额为:1100
取款后的账户余额:800
Bank [账号:s01, 余额:800]

结果和之前不一样了,那是因为,在执行存款方法的存款操作之前进入了休眠,此时取款方法获取了cpu的使用权,先执行了取款操作(1000-200=800),但取款后没来得及更新余额(此时余额还是起初设定的1000)又进入休眠状态,这时存款的休眠时间就到了,又接着执行存款操作(1000+100=1000),所以造成了上面的结果。

若这种情况真正发生在银行中,会造成很严重的后果,存款和取款的线程都对BankThread类的对象进行操作,这种机制称为线程的同步或线程的互斥。

  • 为了保证在存款或取款的时候,不允许其他线程对帐户余额进行操作
  • 需要将Bank对象进行锁定
  • 使用关键字synchronized实现

synchronized同步关键字用在

  • 成员方法 public synchronized void saveAccount(){}
  • 静态方法 public static synchronized void saveAccount(){}
  • 语句块 synchronized (obj){……}

使用该关键字的方法执行完前,其他线程不能够打断它。
使用synchronized对代码进行修改:

//存款
public synchronized void saveAccount(){
    //获取当前的账号余额
    int balance = getBalance();
    
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    //修改余额,存100
    balance += 100;
    //修改账户余额
    setBalance(balance);
    //输出存款后的账户余额
    System.out.println("存款后的账户余额为:" + balance);
}

//取款
public void drawAccount(){
    synchronized(this){
        //获得当前的账户余额
        int balance = getBalance();
        //修改余额,取200
        balance = balance - 200;

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //修改账户余额
        setBalance(balance);
        System.out.println("取款后的账户余额:" + balance);
    }
}

输出:

存款后的账户余额为:1100
取款后的账户余额:900
Bank [账号:s01, 余额:900]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值