关于多线程:Java-锁机制实现多线程售票案例

51次阅读

共计 4072 个字符,预计需要花费 11 分钟才能阅读完成。

本文首发自 [慕课网](imooc.com),想理解更多 IT 干货内容,程序员圈内热闻,欢送关注 ” 慕课网 ” 及“慕课网公众号”!
作者:王军伟 Tech| 慕课网讲师


1. 前言

本文内容次要是应用 Java 的锁机制对多线程售票案例进行实现。售票案例少数状况下次要关注多线程如何平安的缩小库存,也就是残余的票数,当票数为 0 时,进行缩小库存。

本节内容除了关注车票库存的缩小,还会波及到退票窗口,可能更加贴切的模仿实在的场景。

本节内容须要学习者关注如下两个重点:

  • 把握多线程的售票机制模型,在后续的工作中如果波及到相似的场景,可能第一工夫理解场景的整体构造;
  • 应用 Condition 和 Lock 实现售票机制,坚固咱们本章节内容所学习的新的锁机制。

2. 售票机制模型

售票机制模型是源于现实生活中的售票场景,从开始的单窗口售票到多窗口售票,从开始的人工统计票数到后续的零碎智能在线售票。多并发编程可能实现这一售票场景,多窗口售票状况下保障线程的安全性和票数的正确性。

如上图所示,有两个售票窗口进行售票,有一个窗口解决退票,这既是现实生活中一个简略的售票机制。

3. 售票机制实现

场景设计:

  • 创立一个工厂类 TicketCenter,该类蕴含两个办法,saleRollback 退票办法和 sale 售票办法;
  • 定义一个车票总数等于 10,为了不便察看后果,设置为 10。学习者也可自行抉择数量;
  • 对于 saleRollback 办法,当产生退票时,告诉售票窗口持续售卖车票;
  • 对 saleRollback 进行特地设置,每隔 5000 毫秒退回一张车票;
  • 对于 sale 办法,只有有车票就进行售卖。为了更便于察看后果,每卖出一张车票,sleep 2000 毫秒;
  • 创立一个测试类,main 函数中创立 2 个售票窗口和 1 个退票窗口,运行程序进行后果察看。
  • 批改 saleRollback 退票工夫,每隔 25 秒退回一张车票;
  • 再次运行程序并察看后果。

实现要求:本试验要求应用 ReentrantLock 与 Condition 接口实现同步机制。

实例:

public class DemoTest {public static void main(String[] args) {TicketCenter ticketCenter = new TicketCenter();
            new Thread(new saleRollback(ticketCenter),"退票窗口"). start();
            new Thread(new Consumer(ticketCenter),"1 号售票窗口"). start();
            new Thread(new Consumer(ticketCenter),"2 号售票窗口"). start();}
}

class TicketCenter {
    private int capacity = 10; // 依据需要:定义 10 涨车票
    private Lock lock = new ReentrantLock(false);
    private Condition saleLock = lock.newCondition();
    // 依据需要:saleRollback 办法创立,为退票应用
    public void saleRollback() {
        try {lock.lock();
            capacity++;
            System.out.println("线程 ("+Thread.currentThread().getName() + ") 产生退票。" + "以后残余票数"+capacity+"个");
            saleLock.signalAll(); // 产生退票,告诉售票窗口进行售票} finally {lock.unlock();
        }
    }

    // 依据需要:sale 办法创立
    public void sale() {
        try {lock.lock();
            while (capacity==0) { // 没有票的状况下,进行售票
                try {System.out.println("正告:线程 ("+Thread.currentThread().getName() + ") 筹备售票,但以后没有残余车票");
                    saleLock.await(); // 残余票数为 0,无奈售卖,进入 wait} catch (InterruptedException e) {e.printStackTrace();
                }
            }
            capacity-- ; // 如果有票,则售卖 -1
            System.out.println("线程 ("+Thread.currentThread().getName() + ") 售出一张票。" + "以后残余票数"+capacity+"个");
        } finally {lock.unlock();
        }
    }
}

class saleRollback implements Runnable {
    private TicketCenter TicketCenter; // 关联工厂类,调用 saleRollback 办法
    public saleRollback(TicketCenter TicketCenter) {this.TicketCenter = TicketCenter;}
    public void run() {while (true) {
            try {Thread.sleep(5000);
            } catch (InterruptedException e) {e.printStackTrace();
            }
            TicketCenter.saleRollback(); // 依据需要,调用 TicketCenter 的 saleRollback 办法}
    }
}
class Consumer implements Runnable {
    private TicketCenter TicketCenter;
    public Consumer(TicketCenter TicketCenter) {this.TicketCenter = TicketCenter;}
    public void run() {while (true) {TicketCenter.sale(); // 调用 sale 办法
            try {Thread.sleep(2000);
            } catch (InterruptedException e) {e.printStackTrace();
            }
        }
    }
}

后果验证:

线程 (1 号售票窗口) 售出一张票。以后残余票数 9 个
线程 (2 号售票窗口) 售出一张票。以后残余票数 8 个
线程 (2 号售票窗口) 售出一张票。以后残余票数 7 个
线程 (1 号售票窗口) 售出一张票。以后残余票数 6 个
线程 (1 号售票窗口) 售出一张票。以后残余票数 5 个
线程 (2 号售票窗口) 售出一张票。以后残余票数 4 个
线程 (退票窗口) 产生退票。以后残余票数 5 个
线程 (1 号售票窗口) 售出一张票。以后残余票数 4 个
线程 (2 号售票窗口) 售出一张票。以后残余票数 3 个
线程 (2 号售票窗口) 售出一张票。以后残余票数 2 个
线程 (1 号售票窗口) 售出一张票。以后残余票数 1 个
线程 (退票窗口) 产生退票。以后残余票数 2 个
线程 (1 号售票窗口) 售出一张票。以后残余票数 1 个
线程 (2 号售票窗口) 售出一张票。以后残余票数 0 个
正告:线程 (1 号售票窗口) 筹备售票,但以后没有残余车票
正告:线程 (2 号售票窗口) 筹备售票,但以后没有残余车票
线程 (退票窗口) 产生退票。以后残余票数 1 个
线程 (1 号售票窗口) 售出一张票。以后残余票数 0 个
正告:线程 (2 号售票窗口) 筹备售票,但以后没有残余车票
正告:线程 (1 号售票窗口) 筹备售票,但以后没有残余车票

后果剖析:从后果来看,咱们正确的实现了售票和退票的机制,并且应用了 ReentrantLock 与 Condition 接口。

代码片段剖析 1:看售票办法代码。

public void sale() {
        try {lock.lock();
            while (capacity==0) { // 没有票的状况下,进行售票
                try {System.out.println("正告:线程 ("+Thread.currentThread().getName() + ") 筹备售票,但以后没有残余车票");
                    saleLock.await(); // 残余票数为 0,无奈售卖,进入 wait} catch (InterruptedException e) {e.printStackTrace();
                }
            }
            capacity-- ; // 如果有票,则售卖 -1
            System.out.println("线程 ("+Thread.currentThread().getName() + ") 售出一张票。" + "以后残余票数"+capacity+"个");
        } finally {lock.unlock();
        }
    }

次要来看办法中仅仅应用了 await 办法,因为退票是场景触发的,售票窗口无需唤醒退票窗口,因为实在的场景下,可能没有退票的产生,所以无需唤醒。这与生产者与消费者模式存在着比拟显著的区别。

代码片段剖析 2:看退票办法代码。

public void saleRollback() {
        try {lock.lock();
            capacity++;
            System.out.println("线程 ("+Thread.currentThread().getName() + ") 产生退票。" + "以后残余票数"+capacity+"个");
            saleLock.signalAll(); // 产生退票,告诉售票窗口进行售票} finally {lock.unlock();
        }
    }

退票办法只有 signalAll 办法,告诉售票窗口进行售票,无需调用 await 办法,因为只有有退票的产生,就可能持续售票,没有库存下限的定义,这也是与生产者与消费者模式的一个次要区别。

总结:售票机制与生产者 – 消费者模式存在着轻微的区别,须要学习者通过代码的实现缓缓领会。因为售票办法只须要进入 await 状态,退票办法须要唤醒售票的 await 状态,因而只须要创立一个售票窗口的 Condition 对象。

4. 小结

本文内容次要对售票机制模型进行了解说,核心内容为售票机制的实现。实现的过程应用 ReentrantLock 与 Condition 接口实现同步机制,也是本文的重点常识。


欢送关注「慕课网」官网帐号,咱们会始终保持提供 IT 圈优质内容,分享干货常识,大家一起独特成长吧!

本文原创公布于慕课网,转载请注明出处,谢谢合作

正文完
 0