游戏线程池的设计

在前面的章节中,我们一再提到,游戏的业务逻辑复杂,
很多数据都是放在内存中进行处理的,但是,仅仅放在内存中还不能完全满足要求。

我举个例子,房间 A 里面的四个玩家打牌,如果使用普通的 Java 线程池处理这个房间的所有信息,就会带来新的问题:

1 所有消息对于房间信息的修改,需要使用锁,对性能有一定的影响;
2 使用锁之后,业务实现者要时时考虑锁的使用问题,增加了业务开发的难度
3 有序性,我们知道 Java 线程池中任务的流转过程,先看有没有达到核心线程数,再看队列有没有满,最后看有没有达到最大线程数,我举个极端的例子,如果队列满了,此时,发来一条消息,它会去尝试创建一个新的线程(未达最大线程数)来处理当前消息,这导致该房间之前的消息可能还在队列中未处理,而这条消息却先处理了,这明显不符合要求。

基于以上三点原因,使用 Java 线程池肯定是不能满足我们的要求了,那么,使用 Netty 的线程池是否可以呢?

所以,我们需要重新设计一种线程池:不加锁且消息有序,这要怎么实现呢?

我们前面提到过一个名词:房间制,那么,是不是把同一个房间的消息发到同一个线程进行处理就可以了呢?
答案确实是这样的,但是实现起来并没有那么简单。
首先,使用 Java 自带的线程池肯定是不行的了。
其次,使用 Netty 的线程池,通过前面关于 Netty 线程池的源码剖析,我们知道,在 Netty 中,有两种选择线程的方式,即 PowerOfTwoEventExecutorChooser 和 GenericEventExecutorChooser,然而,它们的内部实现并没有本质上的区别,都是通过轮询的方式调用线程池中的线程来处理任务,所以,乍看之下,也不满足我们的要求。

最后,只能自己来实现了?自己实现的话,也是每个线程维护自己的队列,且把同一个房间的消息都到同一个线程的队列中,这其实跟 Netty 还是有些像的,而且,自己实现的线程池,在稳定性、安全性等方面的考量,也是一个非常重要的问题,所以,我们能不能对 Netty 的线程池进行改造,拿过来即用呢?

通过前面的分析,可以发现,其实 Netty 的线程池本身是满足我们的要求的,关键在于选择线程的方式这里,所以,改造的要点就在这里,我们需要自己实现一种线程选择的方式,比如,按房间号去选择线程。

private static final class PowerOfTwoEventExecutorChooser implements EventExecutorChooser {private final AtomicInteger idx = new AtomicInteger();private final EventExecutor[] executors;PowerOfTwoEventExecutorChooser(EventExecutor[] executors) {this.executors = executors;}@Overridepublic EventExecutor next() {return executors[idx.getAndIncrement() & executors.length - 1];}
}

在调用 next () 方法选择线程的时候,似乎不能传递额外的参数了,但是,别忘了,我们还有终极杀器 —— 线程本地变量,在调用 next () 方法之前,可以把房间号存储在 FastThreadLocal 中,这样在 next () 中就能获取到这个房间号,然后,就可以使用房间号来做路由了,比如按房间号取模,完美 ^^

游戏线程池的实现
参考 DefaultEventExecutorChooserFactory 的代码,我们实现一个自己用的 MahjongEventExecutorChooserFactory,并将其运用到自定义的线程池 MahjongEventExecutorGroup 中:

public class MahjongEventExecutorGroup extends MultithreadEventExecutorGroup {private MahjongEventExecutorGroup(int nThreads) {// 使用自定义的选择器工厂super(nThreads, null, new MahjongEventExecutorChooserFactory(), null);}@Overrideprotected EventExecutor newChild(Executor executor, Object... args) throws Exception {return new DefaultEventExecutor(this, executor);}// 工厂类private static class MahjongEventExecutorChooserFactory implements EventExecutorChooserFactory {@Overridepublic EventExecutorChooser newChooser(EventExecutor[] executors) {return new MahjongEventExecutorChooser(executors);}// 真正使用的选择器private static class MahjongEventExecutorChooser implements EventExecutorChooserFactory.EventExecutorChooser {private final AtomicInteger idx = new AtomicInteger();private final EventExecutor[] executors;MahjongEventExecutorChooser(EventExecutor[] executors) {this.executors = executors;}@Overridepublic EventExecutor next() {// 从上下文中取出当前的房间idLong roomId = MahjongContext.currentContext().getCurrentRoomId();long id;if (roomId != null) {id = roomId;} else {// 没获取到房间号的消息轮徇扔到业务线程池中处理// 他们往往跟房间信息没啥关系,比如登录请求id = idx.getAndIncrement();}// 根据id取模选择线程执行return executors[(int) (id & executors.length - 1)];}}}
}

游戏线程池的设计0-转自相关推荐

  1. 【重难点】【JUC 05】线程池核心设计与实现、线程池使用了什么设计模式、要你设计的话,如何实现一个线程池

    [重难点][JUC 05]线程池核心设计与实现.线程池使用了什么设计模式.要你设计的话,如何实现一个线程池 文章目录 [重难点][JUC 05]线程池核心设计与实现.线程池使用了什么设计模式.要你设计 ...

  2. 线程池的设计(二):领导者追随者线程池的设计

    2019独角兽企业重金招聘Python工程师标准>>> 接上文:线程池的设计(一):半同步半异步线程池的设计 领导者追随者模式在使用方式上与半同步半异步模式相同,以下主要介绍不同的实 ...

  3. 【C++ 并发 线程池设计】深入理解C++线程池:设计、实现与应用

    目录标题 1. 引言 2. 线程池类设计 2.1 线程池类的主要成员变量 2.2 线程池类的构造函数和析构函数 3. 线程池任务的添加与执行 3.1 如何添加任务到线程池 3.2 如何在线程池中执行任 ...

  4. Linux线程池的设计

    我设计这个线程池的初衷是为了与socket对接的.线程池的实现千变万化,我得这个并不一定是最好的,但却是否和我心目中需求模型的.现把部分设计思路和代码贴出,以期抛砖引玉.个人比较喜欢搞开源,所以大家如 ...

  5. Linux多线程实践(9) --简单线程池的设计与实现

    线程池的技术背景 在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内存资源或者其它更多资源.在Java中更是如此,虚拟机将试图跟踪每一个对象,以便能够在对象销毁后进行垃圾回收.所以 ...

  6. JAVA线程池 -clt设计与分析

    1. 前言 ctl 是线程池源码中常常用到的一个变量. 它的主要作用是记录线程池的生命周期状态和当前工作的线程数. 作者通过巧妙的设计,将一个整型变量按二进制位分成两部分,分别表示两个信息. 2. 源 ...

  7. 你还不懂线程池的设计及原理吗?掰开揉碎了教你设计线程池

    大家在学习线程池的时候也曾查阅过各种资料,但是感觉大佬写的很好但是写的不够详细,写的详细的设计思路又很简单,所以我的出发点就是让读者可以清晰明确的看懂整个设计思想和设计过程,可以举一反三,在今后内存池 ...

  8. 线程池设计中的惊群问题

    2019独角兽企业重金招聘Python工程师标准>>> 多线程编程已经是现在网络编程中常用的编程技术,设计一个良好的线程池库显得尤为重要.在 UNIX(WIN32下可以采用类似的方法 ...

  9. idhttpserver是按线程接受请求的吗_1000个并发线程,10台机器,每台机器4核,设计线程池大小...

    一道面试题 兄弟们,怎么说? 我觉得如果你工作了两年左右的时间,或者是突击准备了面试,这题回答个八成上来,应该是手到擒来的事情.这题中规中矩,考点清晰,可以说的东西不是很多. 但是这都上血书了,那不得 ...

最新文章

  1. Android常用逆向工具总结(未完待续)
  2. 0基础学python编程难吗-对于0基础的人,直接学Python编程合适吗?
  3. P3390矩阵快速幂
  4. Jersey框架三:Jersey对HTTPS的支持
  5. web.xml上下文配置
  6. JavaFX将Node导出为图片
  7. WINCE下I/O操作基础
  8. Nginx 架构详解
  9. 苹果Mac专业的字体管理应用:​​​​RightFont
  10. 用matlab进行拉普拉斯滤波,[转载]matlab滤波技术及区域处理---线性滤波
  11. Java Caledar类(日历类)判断本周周数
  12. MATLAB图形绘制--添加图例
  13. php 中大括号,PHP中大括号#39;{}#39;用法实例总结
  14. vue项目使用3d高德城市地图用法
  15. 面试题65:什么是虚拟dom
  16. LeetCode日常刷题1、657、717、67
  17. python 3.7安装festtext、提示要更新_现代开发者必备:5个更流畅、更受欢迎的Python web框架...
  18. Android画板开发(二) 橡皮擦实现
  19. 观览世界格局——蒙古风币wsec新动作
  20. 安装CentOS7虚拟机(超详细)

热门文章

  1. 声明式事务、Spring 中常用注解、Ajax
  2. Linux性能优化-磁盘RAID
  3. python的异步网络编程_python异步网络编程怎么使socket关闭之后立即执行一段代码?...
  4. JavaScript——闭包函数及拓展题目
  5. DSF深搜入门全排列笔记nyoj366
  6. 雷军宣布:启动小米成立以来最大组织架构变革(附内部邮件原文)
  7. 使Jackson和Mybatis支持JSR310标准
  8. SQL Server Profiler
  9. cocos2d-x物业现场
  10. 为CheckBoxList每个项目添加一张图片