编辑
🌟 大家好,我是摘星! 🌟
今天为大家带来的是并发编程中的经典对象复用设计模式-享元模式,废话不多说让我们直接开始。
目录
8. 享元模式
享元模式是一种结构型设计模式,它旨在通过共享相同类型且相同值的对象来减少内存消耗和对象创建的开销。在Java中,享元模式的实现涉及两个主要角色:享元工厂和享元对象。
角色 |
补充说明 |
示例 |
享元工厂 |
应实现为单例模式,使用双重检查线程安全 |
使用静态初始化块保证线程安全 |
享元对象 |
必须设计为不可变类,所有属性用 修饰 |
返回的对象不可变 |
外部状态 |
需通过方法参数传递,不能存储在享元对象中 |
的运算结果每次创建新对象 |
8.1. 享元模式体现
8.1.1. 包装类
在JDK中Boolean
Byte
Short
Integer
Long
Character
等包装类提供了valueOf方法,这个方法就使用到了享元模式
- 例如Long的valueOf会缓存-128~127之间的Long对象,在这个范围之间会重用对象,大于这个范围,才会新建Long对象
Byte
Short
Integer
Long
的缓存范围都是-128~127,Integer的最小值不能变,但是最大值可以调整JVM参数调整Boolean
缓存了TRUE和FALSECharacter
缓存范围是0~127
private static class LongCache { private LongCache(){} static final Long cache[] = new Long[-(-128) + 127 + 1]; static { for(int i = 0; i < cache.length; i++) cache[i] = new Long(i - 128); } } public static Long valueOf(long l) { final int offset = 128; if (l >= -128 && l <= 127) { // will cache return LongCache.cache[(int)l + offset]; } return new Long(l); }
包装类 |
默认缓存范围 |
可调整参数 |
线程安全实现 |
Integer |
-128~127 |
|
静态final数组 |
Long |
-128~127 |
不可调整 |
类加载时初始化 |
Character |
0~127 |
不可调整 |
静态代码块初始化 |
Boolean |
TRUE/FALSE |
不可调整 |
静态final常量 |
8.1.2. 字符串常量池
// 编译器优化示例 String s1 = "摘星"; // 直接存入常量池 String s2 = new String("摘星").intern(); // 手动入池 // 内存结构图示 ┌───────────┐ ┌───────┐ | 堆 | ←──| 引用 | | String对象| └───────┘ └───────────┘ ↑ │ ┌───────────┐ ┌───┴───┐ | 方法区 | | 字面量 | | 常量池 | ←──| "摘星" | └───────────┘ └───────┘
// 基准测试结果(纳秒/操作) +-------------------+----------+----------+ | 操作类型 | JDK8 | JDK17 | +-------------------+----------+----------+ | 常量池查找 | 15 | 12 | | 新建String对象 | 85 | 78 | | intern()调用 | 120 | 95 | +-------------------+----------+----------+
8.1.3. BigDecimal和BigInteger
// 反例:BigDecimal未使用享元 BigDecimal a = new BigDecimal("10.00"); // 每次新建对象 BigDecimal b = new BigDecimal("10.00"); // 内存地址不同 // 正例:使用valueOf优化(部分版本缓存0-10) BigDecimal c = BigDecimal.valueOf(10); // 可能返回缓存对象
8.1.4. 静态工厂
public class Money { private static final BigDecimal[] CACHE = new BigDecimal[100]; static { for(int i=0; i<100; i++) { CACHE[i] = new BigDecimal(i); } } public static BigDecimal valueOf(int val) { return (val >=0 && val <100) ? CACHE[val] : new BigDecimal(val); } } public BigDecimal add(BigDecimal augend) { return new BigDecimal(this.value + augend.value); // 伪代码 }
8.2. JVM优化策略
逃逸分析:
// 可能被栈上分配的情况(JDK17实测) void calculate() { Long temp = 127L; // 可能被优化为原始类型 System.out.println(temp); }
缓存策略
策略类型 |
优点 |
缺点 |
适用场景 |
预加载缓存(Integer) |
无竞争 |
内存占用固定 |
值域明确 |
延迟加载(String.intern) |
按需缓存 |
需同步控制 |
字符串去重 |
动态扩展(-XX:AutoBoxCacheMax) |
灵活调整 |
启动后不可变 |
业务特定范围 |