多线程与高并发编程(五)

一、AQS

  1. AQS内的state状态通过CAS来改变
  2. AQS内的队列的节点添加,也是通过compareAndSetTail(CAS)来改变
  3. 双向链表是因为需要看前面节点的状态
      CAS操作是针对的tail也就是链表的尾巴,多个线程想加入链表就不需要整体链表都加sync了,这样效率比较高,下图中oldTail为期望值,node为改为的值,因为;;是死循环,所以不改变则一只自旋。
    在这里插入图片描述

二、VarHandle

  1. JDK1.9开始出现的,单词意思变量句柄,指向某个变量的引用
      例如:Object o = new Object(); 这里的o是一个引用指向Oject开辟的空间,varHandle也代表这个引用。

  2. 如下图handle声明用法:
      MethodHandles.lookup()为固定写法不管,findVarHandle的参数分别为那个值所在对象、值的名字、具体对象。这样就会使声明的VarHandle指向这个x的8了。
    在这里插入图片描述

  3. 如下图handle用法:
      handle.get(name) 获取handle指向name的值
      handle.set(name, value) 将name的值修改
      handle.compareAndSet(name, oldValue, newValue) CAS操作进行修改name的值
      handle.getAndAdd(name, addValue) name的值加addValue
    注:
      这些操作都是原子性的,线程安全的!Handle的意义就在此,long类型等不能进行直接的原子操作!
      VarHandle可以理解为直接操纵二进制码,所以效率非常高,9之前用反射操作的方式(每次使用都会自动检查)没有VarHandle快。
    在这里插入图片描述

三、ThreadLocal(强引用,配合四中的弱引用来读,四、中具体讲了ThreadLocal)

  1. 用法
      ThreadLocal tl = new ThreadLocal<>() 这里的T是任意对象
      t1.get() 从ThreadLocal中取
      t1.set(Object) 放入ThreadLocal
    注:
      ThreadLocal中的值和对象只是针对自己线程的。两个线程对同一个t1对象的读写是隔离的。
  2. 源码实现
     ①:以set()方法为例(比如T是一个叫Person的类)
       内部是有一个Map
       ThreadLocal.java:Thread.currentThread()获取了当前线程
    在这里插入图片描述
      这里的getMap(t)进入,发现获取了一个ThreadLocalMap,这个map是在Thread中:
    在这里插入图片描述
      总结上面Thread.currentThread.map(ThreadLocal, person)获取了当前线程中的一个map中,所以其实不是一个map用不同Thread做key的隔离,实际上是不同的Thread用不同的map。
      这个ThreadLocalMap内可以存在多个key:value,key是ThreadLocal,value是set进来的对象,每个ThreadLocal只能set自己的,那么代表这个map是全局通用的,每个声明的ThreadLocal.set()时,都会对同一个map.put()内容。
  3. ThreadLocal用途
      声明式事务,保证同一个connection(例如配置中有四项都是要连接数据库,就可以合并成为一个事务,但是不同的连接之间肯定不能合并为一个事务,那么第一个取的时候把这个connection放入本地线程的ThreadLocal中,以后的连接实际上都是从这个ThreadLocal中来取,保证是同一个链接,这部分知识应该是基础中的)

四、JAVA中的引用

引用一共四种:强、软、弱、虚

  1. 强引用:
      普通的引用就是强引用(Object o = new Object())
      垃圾回收时,有引用指向的空间一定不会被回收,也就是强引用不会被垃圾回收,除非下面你Object o = null,然后调用了System.gc(),System.in.read()(这句话是为了阻塞当前线程的,没有特殊含义,因为gc是跑在别的线程中的,这样阻塞一下main的线程,防止main结束,要是main结束了,gc也没啥意义了)
  2. 软引用:
      一个对象若只有一个软引用指向他,只有当系统堆内存不够用时才会被回收。
      SoftReference。下图中m指向了一个softReference软引用,这个软引用指向了一个字节数组。
    在这里插入图片描述
      用途:做缓存用,比如说从数据库取了一堆数据,需要用直接存缓存取,空间不够了就可以去掉,平时很难用到。
  3. 弱引用(面试重点):
      只要遭遇到gc就会回收
      weakReference。下图中m只想了一个weakReference弱引用,这个弱引用指向了M对象,这个声明之后直接system.gc()就null了。
    在这里插入图片描述
      用途:这个M对象若除了这个弱引用还有个强引用指向它,那么当强引用消失之后,这个对象就应该被回收。
      一般用在容器中
      在ThreadLocal(基本上下图就是ThreadLocal面试能问到的最深处)中应用(实线是强引用,虚线是弱引用,tl是声明的ThreadLocal对象):
    在这里插入图片描述
    在这里插入图片描述
      ThreadLocal中发现Entry(map中是entry这个key:value)的key实际上是一个弱引用指向ThreadLocal(因为内部的map的key是ThreadLocal),源码如下图(ThreadLocal内):
    在这里插入图片描述
      若key指向ThreadLocal是强引用,那么tl这个ThreadLocal的声明都不指向这个ThreadLocal时,那么因为ThreadLocal有强引用指向他,所以他永远不会被回收,而有些游戏服务器之类的,可能这个Thread永远不会停,那么这块区域就会永远不被回收,这块就发生了内存泄漏,单这个key若是弱引用的话,tl消失时这个指向的ThreadLocal就被回收了,因为tl都不指向这里了,map指向这个ThreadLocal没意义了所以无影响。
      补充知识(内存泄漏:内存中有一块永远不会被回收。 OOM:内存分配不够使了。 完全不同的概念)。
    ThreadLocal的坑:
      key指向的弱引用,回收之后key变成了null,但是这个map永远存在的,所以这个value还是指向了另外的对象,所以ThreadLocal在每次使用完之后一定要手工tl.remove()回收ThreadLocal,否则这个map越来越大之后还是会在发生内存泄漏,慢慢的OOM了!如下图:
    在这里插入图片描述
  4. 虚引用:
      主要是处理堆外内存的。
    在这里插入图片描述
       PhantomReference,声明的条件苛刻,第二个必须是一个队列。
    在这里插入图片描述
      虚引用被垃圾回收发现直接就会被干掉,被干掉时放入声明时的那个队列中一个值,检测队列中出现了值就代表虚引用被干掉了,往队列中扔一个值就是虚引用被干掉通知的方式。
      PhantomReference.get(),无论什么时候都拿不到值(不像上面那几个有引用就能拿到,这里有了也拿不到)。
      用途:基本用不上,给写JVM人用的,写netty之类的也可以,一般他们都会监控这个队列,发现被通知之后就会做出一些处理
    在这里插入图片描述
      本四大项中虚引用第一张图,DirectByteBuffer是NIO中比较新的一个东西叫直接内存,也叫堆外内存,这部分不会被JVM虚拟机来调用,而是OS直接调用,因为这部分已经不属于JVM管辖,所以垃圾回收永远管不到他,所以当虚引用被垃圾回收时,我们监控发现队列中出现了值说明被回收了,那么我们手动去DirectByteBuffer也就是堆外内存中去回收那块没人指向的空间。

五、堆外内存的回收(超纲啦):

  若是用的C、C++写的JVM那么用delete()或者free() C C++的函数。
  JAVA中现在也有直接操作的,这个类叫Unsafe,不过新的JDK版本似乎不能直接访问了,回收内存用unsafe.freeMemory(long),分配内存用unsafe.allocateMemory(long),JUC的底层compareAndSwap都用到了这个类。
在这里插入图片描述
  补充知识:垃圾回收不调用gc的话,一般来讲满了才会出来检查清扫。

北京马士兵教育学习笔记整理

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值