深入理解与应用多线程技术( 四 )

  • 所以 , 如果同一个Cache line的内容被多个线程读写,就很容易产生相互竞争,频繁回写主内存,会大大降低性能 。
  • 解决伪共享问题的一种方法是通过填充(Padding)来确保共享的变量独立存储于不同的缓存行中 。填充的思想是在变量之间插入一些无关的数据,使它们分布到不同的缓存行,从而避免多个变量共享同一个缓存行 。
    在Java中,可以使用@Contended注解来避免伪共享 。这个注解可以在字段上使用,它会在字段的前后插入填充,使得字段单独占据一个缓存行 。
    Fork/Join框架Fork/Join框架是Java7提供的一个用于并行执行任务的框架,是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架 。
    Fork/Join框架需要理解两个点,「分而治之」和「工作窃取」 。
    分而治之
    深入理解与应用多线程技术

    文章插图
    图片
    工作窃取
    深入理解与应用多线程技术

    文章插图
    图片
    一般就是指做得快的线程(盗窃线程)抢慢的线程的任务来做,同时为了减少锁竞争 , 通常使用双端队列 , 即快线程和慢线程各在一端 。
    ThreadLocal原理ThreadLocal的内存结构图:
    深入理解与应用多线程技术

    文章插图
    图片
    • Thread线程类有一个类型为ThreadLocal.ThreadLocalMap的实例变量threadLocals , 即每个线程都有一个属于自己的ThreadLocalMap 。
    • ThreadLocalMap内部维护着Entry数组 , 每个Entry代表一个完整的对象,key是ThreadLocal本身,value是ThreadLocal的泛型值 。
    • 并发多线程场景下,每个线程Thread,在往ThreadLocal里设置值的时候,都是往自己的ThreadLocalMap里存,读也是以某个ThreadLocal作为引用 , 在自己的map里找对应的key,从而可以实现了线程隔离 。
     
    内存泄露问题:指程序中动态分配的堆内存由于某种原因没有被释放或者无法释放,造成系统内存的浪费 , 导致程序运行速度减慢或者系统奔溃等严重后果 。内存泄露堆积将会导致内存溢出 。
    ThreadLocal的内存泄露问题一般考虑和Entry对象有关,ThreadLocal::Entry被弱引用所修饰 。JVM会将弱引用修饰的对象在下次垃圾回收中清除掉 。这样就可以实现ThreadLocal的生命周期和线程的生命周期解绑 。但实际上并不是使用了弱引用就会发生内存泄露问题,考虑下面几个过程:
    深入理解与应用多线程技术

    文章插图
    图片
    当ThreadLocal Ref被回收了,由于在Entry使用的是强引用 , 在Current Thread还存在的情况下就存在着到达Entry的引用链,无法清除掉ThreadLocal的内容,同时Entry的value也同样会被保留;也就是说就算使用了强引用仍然会出现内存泄露问题 。
    深入理解与应用多线程技术

    文章插图
    图片
    当ThreadLocal Ref被回收了 , 由于在Entry使用的是弱引用,因此在下次垃圾回收的时候就会将ThreadLocal对象清除 , 这个时候Entry中的KEY=null 。但是由于ThreadLocalMap中任然存在Current Thread Ref这个强引用,因此Entry中value的值任然无法清除 。还是存在内存泄露的问题 。
    AQS实现原理 
    AbstractQueuedSynchronizer(AQS)是Java中用于构建同步器的基础框架 。它提供了一个灵活的、可重用的同步器实现,可以用来构建各种同步工具 , 如ReentrantLock、Semaphore、CountDownLatch等 。AQS的核心思想是基于FIFO等待队列,通过状态(state)来管理线程的同步 。
    核心原理: