Immutability模式(避免写)
不变性模式
不变性就是对象一旦被创建之后就不可以被改变,只有读操作,没有写操作。那么就线程安全
无状态也是线程安全,无状态意思就是没有属性,没有属性就不能修改,那么就线程安全了,性能好
不可变性的类:
- class 和 属性加final
- 类里面没有修改方法,只有读方法
- 属性如果是对象,也需要这个属性的对象 满足前面两点
- 如果这个属性不满足这点,那么这个类是线程安全的,但是这个属性不是线程安全的
使用不可变性类,如果要改变类的值,方法里面新建一个不可变性类就好啦
就像String replace方法,返回的是新的String
为了减少不可变性类对象的数量,可以用享元模式
- 享元模式就是一个对象池,新建对象的时候,在对象池看一下有没有这个对象,如果没有就创建,如果有就返回现有的对象
- Long用了一部分享元模式,LongCache对象里面有值为-128~127的对象池,Long调用valueOf方法时,如果在这个范围内,使用LongCache里的对象池返回对象
Copy-on-write
写时复制,适用于读非常多,写非常少且数据量不大的场景
Java里面,cow都是复制整个数据结构的,所以比较耗内存,但是上面的场景读的性能非常好
函数式编程,Java 中的基本数据类型 String、Integer、Long都有用
操作系统的父子进程创建也用到,新建子进程的时候,父子进程公用一个内存地址,后面子进程进行修改时,才会给子进程分配一个内存空间,这也是延时
线程本地存储—ThreadLocal(避免共享)
避免变量共享,一人一个,就不需要对这个变量加锁
局部变量也是避免共享的一种方式,但是并发量高的时候会产生大量的局部变量
调用量=局部变量数,线程数=ThreadLocal数
【还是不太懂ThreadLocal的内存泄露,不过用过之后删掉数据就不会泄露了】
Thread-Per-Message模式:最简单实用的分工方法
就是一个主线程接收请求,每一个新来的任务都创建一个新的线程
在Java里面,新建线程成本很高,而且并发量大很容易oom
Thread-Per-Message 模式会为每一个任务都创建一个线程,在高并发场景中,很容易导致应用 OOM,那有什么办法可以快速解决呢
只能改jvm内存配置,增大jvm新生代的大小,长期解决,引入NIO或AIO,netty 就是这么干的
Worker 丨Thread模式:如何避免重复创建线程?
其实就是线程池,worker thread 就相当于工人,线程池里的线程,一直在队列里面拿任务执行
两阶段终止模式:优雅地终止线程
直接终止线程十分生硬,强行终止线程
两阶段终止模式是先用interrupt()方法,把线程从休眠状态唤醒成运行状态【第一阶段:发出终止指令】。然后线程再自己执行run方法,执行到设置的标志位时自行终止【第二阶段:相应终止指令】
(线程池的shutdown方法终止的时候,还在执行的线程和队列里的线程还可以执行。shutdownNow是强行终止所有线程)
【catch 线程的 的中断异常之后,会重置线程的中断状态,直接使用线程的isinterrupt方法结果是没有中断。所以需要在catch中断异常之后,重新设置中断状态】
示例:
【terminated 标志位 要加volatile,因为start方法里面新建了一个线程,导致stop方法有可见性问题,要加volatile。原子性问题不用考虑】
class Proxy {
// 线程终止标志位
volatile boolean terminated = false;
boolean started = false;
// 采集线程
Thread rptThread;
// 启动采集功能
synchronized void start(){
// 不允许同时启动多个采集线程
if (started) {
return;
}
started = true;
terminated = false;
rptThread = new Thread(()->{
while (!terminated){
// 省略采集、回传实现
report();
// 每隔两秒钟采集、回传一次数据
try {
Thread.sleep(2000);
} catch (InterruptedException e){
// 重新设置线程中断状态
Thread.currentThread().interrupt();
}
}
// 执行到此处说明线程马上终止
started = false;
});
rptThread.start();
}
// 终止采集功能
synchronized void stop(){
// 设置中断标志位
terminated = true;
// 中断线程 rptThread
rptThread.interrupt();
}
}
生产者消费者模式
线程池,队列都属于这种
好处:支持异步,可以平衡消费者生产者速度差异,支持批量任务(比如写日志,可以先一条一条排到队列,然后再批量批量地写入磁盘,提高效率)