网课链接: 黑马程序员java并发.
第七章:共享模型 不可变
第七章 共享模型_不可变
章节总结
- 不可变类型使用
- 不可变类设计
- 原理
- final
- 模式方面
- 享元模式
7.1 日期转换的问题
SimpleDateFormat是线程不安全的, 大几率出现不正确的日期解析结果
同步锁
虽能解决问题,但带来的是性能上的损失,并不算很好
不可变对象
如果一个对象在不能够修改其内部状态(属性),那么它就是线程安全的. 例如在 Java 8 后,提供了一个新的日期格式化类 DateTimeFormatter
7.2 不可变设计
final的使用
发现该类, 类中所有属性都是final的
- 属性用final修饰保证了该属性是只读的代表着不能修改
- 类用final修饰保证了该类中的方法不能被覆盖, 防止子类无意间破坏不可变性
final 原理
设置final变量的原理
public class TestFinal{
final int a = 20;
}
字节码
0: aload_0
1: invokespecial #1
4: aload_0
5: bipush 20
7: putfield #2
<-- 写屏障
10: return
发现final变量的赋值也会通过putfield
指令来完成, 同样在这条指令之后也会加入写屏障, 避免其他线程读取到final初始化阶段, 值有可能为0的情况
保护性拷贝 defensive copy
substring的实现
底层实现中也是首先调用了String创建新的字符串, 然后通过更改 char []value
对其进行更改.
享元模式 flyweight pattern
定义 英文名称:Flyweight pattern. 当需要重用数量有限的同一类对象时
wikipedia: A flyweight is an object that minimizes memory usage by sharing as much data as possible with other similar objects
出自 “Gang of Four” design patterns
归类为 Structual patterns
- 最小化内存使用
- 对相似对象进行共享
包装类, bigInteger, bigDecimal都是不可变类, 皆为线程安全的, 但是先前atomicReference那也用在了bigInteger, bigDecimal上, 这是因为多个操作导致的线程不安全
体现
包装类
在JDK中Boolean, Byte, Short, Integer, Long, Character
例如 Long 的 valueOf 会缓存 -128~127 之间的 Long 对象,在这个范围之间会重用对象,大于这个范围,才会新建 Long 对象:
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);
}
Byte, Short, Long 缓存的范围都是 -128~127
Character 缓存的范围是 0~127
Integer 的默认范围是 -128~127,最小值不能变,但最大值可以通过调整虚拟机参数 "-Djava.lang.Integer.IntegerCache.high "来改变
Boolean 缓存了 TRUE 和 FALSE
String 串池
BigInteger BigDecimal
DIY - 数据库连接池
视频里所提及的实现案例没有考虑:
- 连接的动态增长与收缩
- 连接保活(可用性检测)
- 等待超时处理
- 分布式 hash
对于关系型数据库,有比较成熟的连接池的实现,例如 c3p0、druid 等
对于更通用的对象池,可以考虑用 apache commons pool,例如 redis 连接池可以参考 jedis 中关于连接池的实现。
7.3 无状态
在 web 阶段学习时,设计 Servlet 时为了保证其线程安全,都会有这样的建议,不要为 Servlet 设置成员变量,这 种没有任何成员变量的类是线程安全的
因为成员变量保存的数据也可以称为状态信息,因此没有成员变量就称之为【无状态】