zoukankan      html  css  js  c++  java
  • Java:多线程,使用同步锁(Lock)时利用Condition类实现线程间通信

    如果程序不使用synchronized关键字来保证同步,而是直接使用Lock对象来保证同步,则系统中不存在隐式的同步监视器,也就不能用wait()、notify()、notifyAll()方法进行线程通信了。当使用Lock对象来保证同步时,Java提供了Condition类来协调线程间的通信。

    本示范简单模拟银行帐户的存取款活动,帐户余额大于等于取款金额时允许取款;帐户余额小于1000时允许存款(这与真实业务逻辑不符合,只是技术上需要才如此做的,否则存款一下子全存完就不好玩了)。

    1. 实体Account类

    package com.clzhang.sample.thread;
    
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class Account {
        // 锁对象
        private final Lock lock = new ReentrantLock();
        // Condition对象
        private final Condition condDeposit = lock.newCondition();
        private final Condition condWithdraw = lock.newCondition();
    
        // 为避免double类型计算的误差,balance类型设计为int的
        private int balance;
        
        public Account(int balance) {
            this.balance = balance;
        }
    
        public void withdraw(int drawAmount) {
            // 加锁
            lock.lock();
            try {
                // 如果帐户余额不足,则取钱方法阻塞
                while (balance < drawAmount)
                    condWithdraw.await();
    
                // 执行取钱
                balance -= drawAmount;
                System.out.println(Thread.currentThread().getName() + " 取钱:" + drawAmount + "账户余额为:"
                        + balance);
    
                // 唤醒存款线程
                condDeposit.signal();
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    
        // 按照目前设计,帐户余额大于1000后不让存款,必须先取款后才能再存。
        // 这与真实业务逻辑不符合,只是技术上需要才如此做的,否则存款一下子全存完就不好玩了。
        public void deposit(int depositAmount) {
            lock.lock();
            try {
                // 如果帐户余额大于1000,存钱方法阻塞
                while (balance > 1000)
                    condDeposit.await();
    
                // 执行存款
                balance += depositAmount;
                System.out.println(Thread.currentThread().getName() + " 存款:" + depositAmount + "账户余额为:"
                        + balance);
    
                // 唤醒取款线程
                condWithdraw.signal();
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }

    2. 调用类(DepositDrawTest类)

    package com.clzhang.sample.thread;
    
    class DrawThread extends Thread {
        // 模拟用户账户
        private Account account;
        // 每次取钱数
        private int drawAmount;
    
        public DrawThread(String name, Account account, int drawAmount) {
            super(name);
            this.account = account;
            this.drawAmount = drawAmount;
        }
    
        @Override
        public void run() {
            for (int i = 0; i < 3; i++) {
                account.withdraw(drawAmount);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                }
            }
        }
    }
    
    class DepositThread extends Thread {
        // 模拟用户账户
        private Account account;
        // 每次存钱数
        private int depositAmount;
    
        public DepositThread(String name, Account account, int depositAmount) {
            super(name);
            this.account = account;
            this.depositAmount = depositAmount;
        }
    
        @Override
        public void run() {
            for (int i = 0; i < 3; i++) {
                account.deposit(depositAmount);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                }
            }
        }
    }
    
    public class DepositDrawTest {
    
        public static void main(String[] args) {
            // 创建一个账户,初始帐户余额为0
            Account acct = new Account(0);
    
            // 注意下面的取款与存款的balance参数值需要匹配,
            // 否则可能造成存款过多而不让存,然后又没有人取款导致程序无法正常终止的问题。
            new DrawThread("取钱者1", acct, 400).start();
            new DrawThread("取钱者2", acct, 600).start();
            new DepositThread("存款者甲", acct, 600).start();
            new DepositThread("存款者乙", acct, 200).start();
            new DepositThread("存款者丙", acct, 400).start();
        }
    }

    输出:

    存款者甲 存款:600账户余额为:600
    存款者乙 存款:200账户余额为:800
    存款者丙 存款:400账户余额为:1200
    取钱者1 取钱:400账户余额为:800
    取钱者2 取钱:600账户余额为:200
    存款者乙 存款:200账户余额为:400
    存款者甲 存款:600账户余额为:1000
    取钱者2 取钱:600账户余额为:400
    存款者丙 存款:400账户余额为:800
    取钱者1 取钱:400账户余额为:400
    存款者甲 存款:600账户余额为:1000
    存款者丙 存款:400账户余额为:1400
    取钱者1 取钱:400账户余额为:1000
    存款者乙 存款:200账户余额为:1200
    取钱者2 取钱:600账户余额为:600

    3. 总结

    1. 如果取款金额大于余额则不让取款,等存款队列继续存钱,余额足够支付时再让取款。
    2. 如果存款过多(大于1000),则存款不让存了,等取款队列把钱取走,余额降低到1000以下时,可以继续存款。
    3. 这样就允许多次连续取款(只要帐户有钱),多次连续存款(余额不能大于1000),而不是存款、取款依次调用。 
  • 相关阅读:
    接口测试基础理论
    Python 接口测试requests.post方法中data与json参数区别
    将博客搬至CSDN
    [设计模式] 设计模式课程(二十)--命令模式(Command)
    [设计模式] 设计模式课程(十三)-- 代理模式
    [设计模式] 设计模式课程(十一)-- 享元模式(Flyweight)
    [设计模式] 设计模式课程(十二)-- 门面模式(Facade)
    [设计模式] 设计模式课程(十七)--组合模式
    [设计模式] 设计模式课程(六)-- 桥接模式
    [设计模式] 读懂UML图
  • 原文地址:https://www.cnblogs.com/nayitian/p/3260383.html
Copyright © 2011-2022 走看看