Java偏移量管理模式:实体Serial与Sequence存储方案咨询
适配Serial/Sequence实体的Java数据结构与设计方案
一、核心数据结构选型
- ConcurrentHashMap<String, Entity>:直接用Serial当key,查询、增删实体都是O(1)效率,天生线程安全,适合扩容缩容这类可能多线程操作的场景,完美匹配Serial作为实例唯一标识的需求。
- TreeMap<Integer, Entity>:如果业务需要按Sequence顺序遍历实体,TreeMap会自动按Sequence排序,省去手动排序的麻烦,适合依赖偏移量顺序处理的场景。
- 自定义组合结构:把ConcurrentHashMap(存Serial到实体的映射)和AtomicInteger(维护Sequence自增)结合使用。AtomicInteger的
getAndIncrement()是线程安全的递增操作,生成的Sequence绝不会回退,刚好满足“旧版本Sequence不复用”的要求,同时ConcurrentHashMap保证实体的快速存取。
二、适配业务场景的设计模式
- 单例模式:如果系统只需要一个实体管理入口,把实体管理器做成单例,避免多个管理器各自生成Sequence导致混乱,保证全局Sequence的唯一性。
- 工厂模式:写个专门的
EntityFactory类封装实体创建逻辑——Serial可以用UUID、数据库自增ID或雪花算法生成,Sequence直接用原子类自增,业务代码不用关心ID生成细节,避免到处乱写生成逻辑。 - 备忘录模式:如果需要记录扩容缩容的历史状态(比如紧急回滚),可以把当前实体集合和Sequence的当前值存成“备忘录”,需要时恢复。注意:Sequence一旦生成就不能复用,备忘录里的Sequence只能作为历史记录,不能用来生成新实体。
三、代码示例
1. 实体类定义
public class Entity { private final String serial; // 实例唯一标识,用String方便扩展,也可改用Long类型 private final int sequence; // 递增偏移量,生成后永不复用 public Entity(String serial, int sequence) { this.serial = serial; this.sequence = sequence; } // 仅提供getter,保证ID不可修改 public String getSerial() { return serial; } public int getSequence() { return sequence; } }
2. 实体管理器实现(组合结构+工厂+单例)
import java.util.Collection; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; public class EntityManager { // 单例实例 private static final EntityManager INSTANCE = new EntityManager(); // 存储Serial到实体的映射 private final Map<String, Entity> entityMap = new ConcurrentHashMap<>(); // Sequence自增器,初始值从1开始 private final AtomicInteger sequenceGenerator = new AtomicInteger(1); // 私有构造,禁止外部实例化 private EntityManager() {} public static EntityManager getInstance() { return INSTANCE; } // 创建新实体,自动分配唯一Sequence public Entity createEntity(String serial) { if (entityMap.containsKey(serial)) { throw new IllegalArgumentException("Serial " + serial + "已经存在"); } int sequence = sequenceGenerator.getAndIncrement(); Entity entity = new Entity(serial, sequence); entityMap.put(serial, entity); return entity; } // 删除实体(缩容操作) public void removeEntity(String serial) { entityMap.remove(serial); } // 根据Serial快速获取实体 public Entity getEntityBySerial(String serial) { return entityMap.get(serial); } // 获取当前所有实体 public Collection<Entity> getAllEntities() { return entityMap.values(); } }
3. 业务场景模拟
public class BusinessDemo { public static void main(String[] args) { EntityManager manager = EntityManager.getInstance(); // 初始状态 Entity a1 = manager.createEntity("1"); System.out.println("初始状态: A(Serial=" + a1.getSerial() + ", Sequence=" + a1.getSequence() + ")"); // 扩容 Entity a2 = manager.createEntity("2"); System.out.println("扩容后: A(Serial=" + a1.getSerial() + ", Sequence=" + a1.getSequence() + ")、A(Serial=" + a2.getSerial() + ", Sequence=" + a2.getSequence() + ")"); // 缩容 manager.removeEntity("2"); System.out.println("缩容后: A(Serial=" + manager.getEntityBySerial("1").getSerial() + ", Sequence=" + manager.getEntityBySerial("1").getSequence() + ")"); // 再次扩容 Entity a4 = manager.createEntity("4"); System.out.println("再次扩容后: A(Serial=" + a1.getSerial() + ", Sequence=" + a1.getSequence() + ")、A(Serial=" + a4.getSerial() + ", Sequence=" + a4.getSequence() + ")"); } }
运行输出:
初始状态: A(Serial=1, Sequence=1) 扩容后: A(Serial=1, Sequence=1)、A(Serial=2, Sequence=2) 缩容后: A(Serial=1, Sequence=1) 再次扩容后: A(Serial=1, Sequence=1)、A(Serial=4, Sequence=3)
四、实用注意点
- Serial生成:分布式系统建议用雪花算法生成全局唯一Serial,避免单机自增ID冲突;单机场景用AtomicLong自增也足够。
- Sequence线程安全:必须用原子类(AtomicInteger/AtomicLong)或加锁,多线程下绝对不能让Sequence重复生成。
- 持久化:如果系统重启后需要保留状态,要把entityMap数据和Sequence当前值存到数据库或文件,重启时加载,否则Sequence会从初始值重新开始,违反“不复用”要求。
内容的提问来源于stack exchange,提问作者liotur
相关产品推荐
相关产品推荐

