手K一个游乐园碰碰车的例子,让你读懂线程池原理

写在前面
  • 技术上的概念晦涩难懂,比如多线程并发、线程同步、线程池等,本文旨在把难懂的东西通过现实中的例子讲出来,让技术变得生动。
游乐园碰碰车的案例
游戏规则

先用一张图把规则说清楚:

  • 游玩区最多容纳5组游客同时进行游戏;
  • 游玩区满员时,游客需进入排队等待区等待;
  • 等待区最多容纳5组游客排队等待;
  • 在排队等待区中等待的游客优先进入游玩区进行游戏。
  • 游玩区和排队等待区游客数量满后,其余游客须在入口处进行等待。

在这里插入图片描述

需求分析
  • 创建一个游客类;
  • 创建一个碰碰车游乐区线程池类;
  • 游玩区看作线程池中的游玩线程执行区;
  • 排队等待区看作阻塞队列,游客在队列中排队等待。
  • 游玩线程执行区容量满后,游客需进入阻塞队列中等待;
  • 游玩线程执行区有空闲,等待队列中的游客进入游玩线程执行区中运行。
  • 核心线程池和线程队列都满负荷后,拒绝游客进入。
代码编写
创建一个游客类

设置游客id

/**
 * 用碰碰车的例子读懂线程池原理--游客类
 *
 * @author zhuhuix
 * @date 2020-05-17
 */
public class Visitor   {

    //游客id
    private int  visitorId;

    Visitor(int visitorId) {
        this.visitorId = visitorId;
    }

    public int getVisitorId() {
        return visitorId;
    }

    public void setVisitorId(int visitorId) {
        this.visitorId = visitorId;
    }
    
}

创建一个线程池类
  • 定义一个内部类Player 作为游客游玩线程
  • 设置一个容器HashSet存放游玩区正在游戏的游客线程
  • 设置一个队列BlockingQueue放置等待游戏的游客
  • 创建一个成员方法 public void addPlayer()执行游玩线程;
  • 创建一个成员方法execute作为线程池的执行策略
    – 游玩区人数不满时,游玩线程进入执行
    – 游玩区人数满员且等待区人数不满时,游客进入队列等待
    – 游玩区和等待区都满中时,拒绝游客进入
  • 创建一个主线程main,模拟一系列游客进入游乐区线程池测试。
  • 注意:为了简单说明原理,如果游玩区和等待队列区的总数小于测试主程序中的游客人数时,程序的拒绝策略只是简单的丢弃。可以通过扩大等待队列的容量,或改写测试程序进行更灵活的设置
/**
 * 用碰碰车的例子读懂线程池原理--线程池
 *
 * @author zhuhuix
 * @date 2020-05-17
 */
public class DodgemThreadPool {

    //设置一个常量作为游玩区的容量
    private static final int CORE_POOL_SIZE = 5;
    //设置一个容器存放游玩区正在游戏的游客线程
    private HashSet<Player> corePlayers;

    //设置一个常量作为等待队列的容量
    private static final int WAIT_QUEUE_SIZE = 5;
    //设置一个队列放置等待游戏的游客
    private BlockingQueue<Visitor> queueVisitors;

    //构造函数
    DodgemThreadPool() {
        //创建游玩区容器
        corePlayers = new HashSet<>(CORE_POOL_SIZE);
        //创建等待区队列
        queueVisitors = new LinkedBlockingQueue<>(WAIT_QUEUE_SIZE);
    }

    //设置游戏线程
    class Player implements Runnable {
        @Override
        public void run() {
            while (true) {

                Visitor visitor = null;
                try {
                    visitor = queueVisitors.take();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (visitor != null) {
                    synchronized (this) {
                        int visitorId = visitor.getVisitorId();
                        String name = Thread.currentThread().getName();
                        System.out.println("第"+visitorId+"组游客进入"+name+"开始游戏...");
                        try {
                            TimeUnit.SECONDS.sleep(1);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(name+"的第"+visitorId+"组游客结束游戏");
                        corePlayers.remove(this);

                    }
                }
            }
        }
    }

    //将游客加入游戏进程进行游戏
    public void addPlayer() {
        Player player = new Player();
        corePlayers.add(player);
        Thread thread = new Thread(player, "第" + corePlayers.size() + "号碰碰车");
        thread.start();
    }

    //线程池执行任务策略
    public void execute(Visitor visitor) throws InterruptedException {
        //判断游客是否为空指针
        if (visitor == null) {
            throw new NullPointerException("非法游客!!");
        }
        //如果游玩区有空闲,则将游客线程放入游玩区
        if (corePlayers.size() < CORE_POOL_SIZE) {
            System.out.println("游玩区有空闲位置:"+(CORE_POOL_SIZE-corePlayers.size())+"位!");
            System.out.println("第" + visitor.getVisitorId() + "组游客进入游乐区游戏!");
            queueVisitors.put(visitor);
            addPlayer();
            //如果排队等待区有空闲,则让游客排队等待
        } else if (queueVisitors.size() < WAIT_QUEUE_SIZE) {
            System.out.println("游玩区已满,第" + visitor.getVisitorId() + "组游客进入等待区排队等待...");
            queueVisitors.put(visitor);

            //拒绝游客进入
        } else {
            System.out.println("游乐区游已满负荷运行,请第" + visitor.getVisitorId() + "组游客耐心等待...");

        }

    }

    public static void main(String[] args) throws InterruptedException {

        //创建游乐区线程池
        DodgemThreadPool dodgemThreadPool = new DodgemThreadPool();

        //游客进入游乐区游玩
        for (int i = 1; i <= 20; i++) {
            dodgemThreadPool.execute(new Visitor(i));
        }

    }

}
执行结果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

学以致用

通过手写一个游乐园碰碰车线程池的简单例子,我们基本上搞清楚了线程池中核心区和队列区的基本原理,这对我们在实际工作中通过JDK中的ThreadPoolExecutor类自定义线程池及关键参数的定义与配置有了更好的理解。
具体可以参见java多线程:线程池的深度理解
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

智慧zhuhuix

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值