库存-并发学习笔记 (13)

目前 Copy-on-Write 在 Java 并发编程领域知名度不是很高,很多人都在无意中把它忽视了,但其实 Copy-on-Write 才是最简单的并发解决方案。
它是如此简单,以至于 Java 中的基本数据类型 String、Integer、Long 等都是基于 Copy-on-Write 方案实现的。Copy-on-Write 是一项非常通用的技术方案,
在很多领域都有着广泛的应用。不过,它也有缺点的,那就是消耗内存,每次修改都需要复制一个新的对象出来,好在随着自动垃圾回收(GC)
算法的成熟以及硬件的发展,这种内存消耗已经渐渐可以接受了。所以在实际工作中,如果写操作非常少,那你就可以尝试用一下 Copy-on-Write,效果还是不错的。

思考题

为什么java中没有 CopyOnWriteLinkedList

知识点:链表是分散的存储空间,通过 指针串联起来的

知识点:数组是连续的存储空间

当需要复制数组,只需要复制数组所在的连续空间即可,是一个时间复杂度为O(1)的操作。
而链表是不连续的存储空间,所以复制链表意味着需要 按照链表的指针,遍历整个链表。这将是一个时间复杂度为O(n)的操作。

30 | 线程本地存储模式:没有共享,就没有伤害

笔记

线程封闭

局部变量

ThreadLocal

ThreadLocal源码

切记 ThreadLocalMap是Thread持有的

ThreadLocal

class Thread { //内部持有ThreadLocalMap ThreadLocal.ThreadLocalMap threadLocals; } class ThreadLocal<T>{ public T get() { //首先获取线程持有的 //ThreadLocalMap ThreadLocalMap map = Thread.currentThread().threadLocals; //在ThreadLocalMap中 //查找变量 Entry e = map.getEntry(this); return e.value; } static class ThreadLocalMap{ //内部是数组而不是Map Entry[] table; //根据ThreadLocal查找Entry Entry getEntry(ThreadLocal key){ //省略查找逻辑 } //Entry定义 static class Entry extends WeakReference<ThreadLocal>{ Object value; } } }

ThreadLocal 的内存泄漏问题

一个误区、不是说ThreadLocal一定存在内存泄漏,在一般场景中,Thread持有ThreadLocalMap,ThreadLocalMap以弱引用的方式持有ThreadLocal,当线程Thread被回收,意味着ThreadLocal一定会被回收。

ThreadLocal的内存泄漏发生在配合线程池使用的场景中

在线程池中 线程存活时间很长,往往同应用程序是同生共死的,这就意味着 Thread 持有的 ThreadLocalMap 一直都不会被回收,再加上 ThreadLocalMap 的 Entry对 ThreadLocal的引用是弱引用(WeakReference)

所以 只要 ThreadLocal 结束了自己的生命周期 是可以被回收掉的。但是 Entry 中的value 却是被Entry强引用。所以即便 Value的生命周期结束了,value 也是无法被回收的(可达性分析算法),从而导致内存泄漏

解决方案-一般ThreadLocal配合线程池使用,需要使用try{}finally{}手动释放资源

ExecutorService es; ThreadLocal tl; es.execute(()->{ //ThreadLocal增加变量 tl.set(obj); try { // 省略业务逻辑代码 }finally { //手动清理ThreadLocal tl.remove(); } });

总结

Spring的 线程池管理 采用的就是 ThreadLocal ,每个线程可以针对性的修改自己的线程池。通过这个,可以写一个切面来切换数据源。

线程本地存储模式本质上是一种避免共享的方案,由于没有共享,所以自然也就没有并发问题。

31 | Guarded Suspension模式:等待唤醒机制的规范实现

笔记

等待唤醒机制

31-1

场景:

服务调用方(生产者) 将消息 发动到 MQ。

服务提供方(消费者) 将消息 消费。
现在需要消费者消费完消息后,通知 生产者,然后生产者再响应其客户端。

分析

生产者需要等待

消费者消费完需要通知生产者

方案

分析场景,其实就是一个异步转同步的过程

生产者等待 消费者通知 可以使用 管程实现

管程采用Lock + Condition 实现等待通知的关键 需要找到一个将 生产者发送消息 和 消费者响应消息,将两个消息对应到一个Condition

方案就是将消息对应的Condition缓存起来 这样的话就可以采用 **Key(msg.id) --> value(Condition)

当 上下游 生产者和消费者都是集群化部署,如何解决?

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zygwfg.html