JUC工具

JUC工具:

  • AQS原理【AbstractQueueSychronizer】:阻塞式锁核相关同步器工具的框架

    • state属性:资源状态【独占模式和共享模式】;
      • 可以通过CAS机制来设置state状态;
      • 独占模式,只有一个线程能够访问;
      • 共享模式:可以允许多个线程访问资源;
    • 提供了FIFO的等待队列,类似于Monitor的EntryList;
    • 提供条件变量来是现实等待,唤醒机制,支持多个条件变量,类似于Monitor的WaitSet;
    • 子类树妖实现方法【默认抛异常,UnsupportedOperationException】:
      • tryAcquire
      • tryRelease
      • TryAcquireShared
      • tryReleaseShared
      • isHeldExclusively
  • Reentrantlock:

    • 默认时非公平锁:
      • sync = new NonfairSync();
      • 当获取锁失败后,线程进入acquireQueued逻辑:
        1. acquireQueued会在一个死循环中,不断尝试获取锁,失败后进入park阻塞;
        2. 当前线程为双向链表的第二个节点的时候【第一个节点是逻辑节点,占位】,那么再次tryAcquire尝试获取锁;
        3. 当仍然失败的时,进入shouldParkAfterFailedAcquire逻辑,将前驱node,修改为waitStatus = -1【默认是0,-1:有责任唤醒下一个节点】,并返回false;
        4. 然后再次回到:acquireQueued,然后前驱节点node的waitstatus已经是-1,那么进入parkAndCheckInterrupt【即当前线程】,调用park阻塞;
      • 当多个线程都竞争失败后,这时,拥有者释放锁:
        1. 当前队列不为空,而且头节点的waitStatus = -1,就会唤醒距离头节点最近的一个节点,使用unpark,然后再次尝试获取锁;
        2. 非公平体现:
          1. 当外在又有一个线程在尝试获取锁,回合这个线程一起竞争,
          2. 当线程竞争失败后,会再次进入park阻塞;
    • 可重入:通过线程id对比,然后将锁状态:state++;
    • 释放锁的时候,就是将锁状态:state--;
    • 不可打断模式:
      • 即使被打断,唤醒,也仍然会停留在AQS队列中【只是打断标记被置true,在获取锁后返回】,再次尝试获取锁,只有等待获取锁过后,再能继续运行;
    • 可打断模式:会直接抛出异常的模式,跳出无限循环;
    • 非公平锁:当发现锁状态state = 0,就直接抢锁,不会去检查AQS队列;
    • 公平锁:会先判断AQS队列中的判断,当有对象的时候,不会进行抢锁;
    • 条件变量:每一个条件变量对应的就是一个等待队列,实现类ConditionObject;
      • 也是双向链表;
      • 当调用await是进入ConditionObject的addConditionWaiter,创建新的节点,设置状态为-2,加入等待列表尾部,这个等待列表没有头节点;
      • 接下来进入fullyRelease流程,同步释放锁,唤醒下一个线程并竞争;
      • signal流程:
        • 只有owner线程才能执行唤醒操作;
        • 获取链表的头节点,更改状态为 0,然后加入等待列表
        • 当唤醒不成功时【超时等待】,会接着唤醒下一个节点;
  • ReetranReadWriteLock:读写锁;

    • 读-读:并发,不互斥;
    • 读所不支持条件变量;
    • 读锁不支持锁升级为写锁,不许先释放读锁,才能获取写锁;
    • 写锁支持锁降级,可以在获取写的同时,获取读锁;
    • 读写锁原理:
      • 读写锁用的同一个Sycn同步器,
      • 锁状态:写锁状态:state低16位,读锁状态:state高16位;
      • 读读并发:当遇到共享节点的时,相当于锁的重入,将锁状态state++【一连串的读读线程都会启动,直到遇到一个独享节点】;
  • StampedLock:进一步优化读性能;

    • 配合戳使用;

    • 乐观读锁,只是加了一个标志位,锁:

    • 缺点:

      • 不支持条件变量;
      • 不支持可重入;
      StampedLock stampedLock = new StampedLock();
      long wstamp = stampedLock.writeLock();
      stampedLock.unlock(wstamp);
      //读锁
      long rstamp = stampedLock.tryOptimisticRead(); // 乐观读,获取戳;
      if (stampedLock.validate(rstamp)) { // 戳是否有效;
          return;
      }
      //戳失效了,升级为互斥锁;
      try {
          rstamp  = stampedLock.readLock();
          
      }finally{
          stampedLock.unlock(rstamp);
      }
      
  • Semaphore【信号量】:限制能同时访问共享资源的线程上限;

    • 在高峰期时的限流,单机版,没有考虑分布式;

    • 限制的是线程数,不是限制资源数【比如连接,LimitLatch】;

    • 另一种方法:通过await的阻塞;

      Semaphore semaphore = new Semaphore(3);//3赋值给了AQS中的state;
      try {
          semaphore.acquire();
      } catch (InterruptedException e) {
          e.printStackTrace();
      } finally {
          semaphore.release();
      }
      
  • Future:

    • 获取线程中的返回值;
  • CountdownLatch:

    • 倒数计时,线程直接的同步,相当于join()【底层】;
    • 当AQS中的state=0 的时候,获取锁;
    • 不能重用;
  • CyclicBarrier:

    • 和CountdownLatch用法相同,而且可以复用;
    • 线程数和倒数计数应该一样;

线程安全集合概述:

  • 遗留的安全集合:HashTable【map实现】,Vector【list实现】,早期的线程安全集合,使用synchronied关键词修饰,性能低,而且数量少;

  • 修饰的安全集合:SynchronizedMap,SynchronizedList,都是使用Collections的方法修饰,类似于HashTable, Vector,只是用装饰器设计模式进行设计,使用synchronied修饰;

  • JUC安全集合:

    • Blocking类型:基于锁【ReentrantLock】的实现,并提供阻塞方法;
    • CopyOnWrite类:修改时候,通过拷贝的方式,保护共享数据,通常适用于读多写少,因为写操作比较重;
    • Concurrent的容器:内部使用CAS做优化,具有高并发,但是具有弱一致性:
      • 遍历时弱一致性:当使用迭代器遍历的时候,如果容器发生修改,这时遍历的是旧内容【当使用foreach遍历,会直接抛异常,fail-fast机制,fail-safe机制】;
      • 求大小弱一致性,size操作不一定准;
      • 读取弱一致性;
  • ConcurrentHashMap:

    • 方法是线程安全的,多个安全的组合不能保证原子性;
    • 并发死链【jdk7】
      • 在多线程下,jdk7会将新入节点放入为链表头;
      • jdk8会保持扩容前后顺序,避免的死链,但是会丢数据;
    • 构造器:
      • 懒惰初始化;
        • initTable():使用CAS,其他线程自旋等待;
      • 容量只能是2^n次方;
      • CAS保证原子性;
    • get():
      • 当hash值为正值,则去对应下标取值;
      • 对应头节点是否为空,
        • 当为正数,则为链表
        • 当为负数,则表示1. 其他线程正在扩容【fnode,应该到扩容后列表查找】,2. 表示为树节点;
      • 没有锁;
    • put()方法
      • 不允许有null的键值对,不同方法允许;
      • 当发现为负数【-1】,则帮忙扩容;
      • hash值冲突,对列表的头节点进行加锁;
        • 内部在比较或者红黑树;
    • addCount():对列表进行计数和扩值;
    • size:多线程计算并不准确;
    • 扩容:是直接扩容两倍,以列表为单位,已经迁移的列表方法fnode【hash值为负数】;
  • LinkedBlockingQueue原理:

    • 链表中的next的三种状态:
      • 指向后面节点;
      • 指向自己【出队的时候,帮助GC回收】;
      • null,表示没有后续节点;
      • item可能也是null,0,表示是一个dummy节点;
    • 可以有两把锁,一把控制头,一把控制尾【有dummy节点】;
    • 不允许有空元素;
  • ArrayBLockingQueue

    • 是强制有界,需要提前初始化【不是懒惰初始化】;
    • 只有一个把锁;
  • ConcurrentLinkedQueue:

    • 使用CAS来实现锁;
  • CopyOnWriteArrayList:

    • CopyOnWriteArraySet的本质也是CopyOnWriteArrayList;
    • 底层采用的是写入时拷贝的思想,增删改操作会将底层数据拷贝一份,在拷贝数据上执行,而不影响其他线程的并发读,这里读写分离了;
    • 读操作并没有加锁;
    • get,迭代器的弱一致性;
posted @ 2025-03-30 17:43  烟雨断桥  阅读(6)  评论(0)    收藏  举报