并发编程常见设计方案 不可变模式
并发问题的产生的原因最核心的原因就是 共享 + 读写,如果一个变量被共享,而且还有多个线程对其进行读写,那么就一定会发生并发问题。我们解决问题的思路有两种,一种是根据问题的特点制定相关的解决方案,另外一种是不让问题产生。并发问题的出现一定是,有些线程对共享变量进行读操作,有些线程对同一共享变量进行写操作。就像解决死锁问题一样,破坏死锁的条件就能解决死锁问题,那么如果我们破坏产生并发问题的条件,那么也可以避免并发产生的问题,而今天要介绍的并发设计方案:不可变模式 破坏的就是读写条件,如果多个线程只读的话,就不会有并发问题,简单来说,就是对象一旦被创建之后,就不可被修改,始终保持着初始状态。对应到Java代码中,就是类+final, 类中的属性+final 并只提供可读方法, 在Java中有很多类都是这样的设计,比如基本数据类型的包装类(Long,Integer)还有肯定会使用的String类,这些类的设计都遵守不可变模式,
上面是Java8的代码,可以看到String类的设计符合不可变模式,但是不可变模式不是不能有写操作的方法吗?String类可是有replace()的呀,其实不可变模式的核心只要保证对象的状态从创建开始一直到程序结束都不改变就可以了。如果你看过 replace()的源码,就知道他其实返回的是一个新的String对象
但是不可变模式也是有缺点的,那就是会大量创建对象,导致内存的浪费,于是我们可以通过享元模式,将相同的对象缓存起来,当要继续创建相同的对象时,就不在创建,而是从缓存池中取出来就好了,Java中的基本数据类型包装类就是这样设计的,比如Long类型就会提前缓存[-128,127]的数。
String也会做缓存,只是缓存的都是String的字面量,即写死在代码里的字符串值,比如 String s = “pandaer” “pandaer”就是字面量,他就会被缓存到字符串常量池中,如果后面在代码中还出现“pandaer”的时候用的就是字符串常量池中的同一个对象。
