replace 是通过将替换后的值返回的形式"修改"的字符串,严格来说不能说是修改,而是生成了一个新的字符串。也就是生成了一个新的不可变String对象
TODO,思考 String 类型为什么是存在常量池的
享元模式
享元模式本质上其实就是一个对象池,利用享元模式创建对象的逻辑也很简单:创建之前,首先去对象池里看看是不是存在;如果已经存在,就利用对象池里的对象;
如果不存在,就会新创建一个对象,并且把这个新创建出来的对象放进对象池里。
Long & Integer
Long 这个类并没有照搬享元模式,Long 内部维护了一个静态的对象池,仅缓存了[-128,127]之间的数字,这个对象池在 JVM 启动的时候就创建好了,
而且这个对象池一直都不会变化,也就是说它是静态的。
使用 Immutability 模式的注意事项
对象的所有属性都是 final 的,并不能保证不可变性;
不可变对象也需要正确发布。
注意不可变的边界,如下所示,虽然Bar对象中Foo属性是final的,但是不能保证foo的属性不能被修改。
class Foo{ int age=0; int; } final class Bar { final Foo foo; void setAge(int a){ foo.age=a; } }总结
利用 Immutability 模式解决并发问题,也许你觉得有点陌生,其实你天天都在享受它的战果。Java 语言里面的 String 和 Long、Integer、Double
等基础类型的包装类都具备不可变性,这些对象的线程安全性都是靠不可变性来保证的。
Immutability 模式是最简单的解决并发问题的方法,建议当你试图解决一个并发问题时,可以首先尝试一下 Immutability 模式,看是否能够快速解决。
无状态
具备不变性的对象,只有一种状态,这个状态由对象内部所有的不变属性共同决定。其实还有一种更简单的不变性对象,那就是无状态。
无状态对象内部没有属性,只有方法。除了无状态的对象,你可能还听说过无状态的服务、无状态的协议等等。无状态有很多好处,最核心的一点就是性能。
在多线程领域,无状态对象没有线程安全问题,无需同步处理,自然性能很好;在分布式领域,无状态意味着可以无限地水平扩展,所以分布式领域里面性能的瓶颈一定不是出在无状态的服务节点上。
其实spring的 单例Bean 也是一种无状态的Bean ,也就是在单例Bean中最好不要定义共享变量,因为如果不加入线程安全的控制的话,读写一定能引起并发问题。
29 | Copy-on-Write模式:不是延时策略的COW笔记
Copy-on-Write 写时复制
基本思想-懒惰策略
Copy-On-Write简称COW,是一种用于程序设计中的优化策略。其基本思路是,从一开始大家都在共享同一个内容,当某个人想要修改这个内容的时候,
才会真正把内容Copy出去形成一个新的内容然后再改,这是一种延时懒惰策略。
基本思想-读写分离
当修改容器时,不首先修改原容器,而是将原容器copy一份,然后修改copy的容器,之后再将修改后的容器替换原容器。这样读和写操作的永远都是两个容器。
也就实现了读写分离。
Copy-on-Write 在操作系统中的应用
类 Unix 的操作系统中创建进程的 API 是 fork(),传统的 fork() 函数会创建父进程的一个完整副本,例如父进程的地址空间现在用到了 1G 的内存,那么 fork() 子进程的时候要复制父进程整个进程的地址空间(占有 1G 内存)给子进程,这个过程是很耗时的。而 Linux 中的 fork() 函数就聪明得多了,fork() 子进程的时候,并不复制整个进程的地址空间,而是让父子进程共享同一个地址空间;只用在父进程或者子进程需要写入的时候才会复制地址空间,从而使父子进程拥有各自的地址空间。
java中的 CopyOnWriteArrayList 和 CopyOnWriteArraySet是一种典型的空间换时间的做法
java中的CopyOnWriteXXX容器采用的都是当元素发生变化,就进行copy操作。
存在的问题:当容器非常大,这样就会浪费很多的空间。
总结