线程协作工具与并发容器
目录
线程协作工具类
线程协作工具类,控制线程协作的工具类,帮助程序员让线程之间的协作变得更加简单
CountDownLatch计数门闩:
- 倒数结束之前,一直处于等待状态,直到数到0,等待线程才继续工作。
- 场景:购物拼团、分布式锁
- 方法:
- new CountDownLatch(int count)
- await():调用此方法的线程会阻塞,支持多个线程调用,当计数为0,则唤醒线程
- countdown():其他线程调用此方法,计数减1
Semaphore信号量:
- 限制和管理数量有限的资源的使用
- 场景:Hystrix、Sentinel限流
- 方法:
- new Semaphore ((int permits) 可以创建公平的非公平的策略
- acquire():获取许可证,获取许可证,要么获取成功,信号量减1,要么阻塞等待唤醒
- release():释放许可证,信号量加1,然后唤醒等待的线程
CyclicBarrier循环栅栏:
- 线程会等待,直到线程到了事先规定的数目,然后触发执行条件进行下一步动作
- 场景:并行计算
- 方法:
- new CyclicBarrier(int parties, Runnable barrierAction)参数1集结线程数,参数2凑齐之后执行的任务
- await():阻塞当前线程,待凑齐线程数量之后继续执行
Condition接口:
- 控制线程的“等待”和“唤醒”
- 方法:
- await():阻塞线程
- signal():唤醒被阻塞的线程
- signalAll()会唤起所有正在等待的线程。
- 注意:
- 调用await()方法时必须持有锁,否则会抛出异常
- Condition和Object#await/notify方法用法一样,两者await方法都会释放锁
对比
并发容器
什么是并发容器? 针对多线程并发访问来进行设计的集合,称为并发容器
- JDK1.5之前,JDK提供了线程安全的集合都是同步容器,线程安全,只能串行执行,性能很差。
- JDK1.5之后,JUC并发包提供了很多并发容器,优化性能,替代同步容器
CopyOnWriteArrayList
- CopyOnWriteArrayList底层数组实现,使用复制副本进行有锁写操作,适合读多写少,允许短暂的数据不一致的场景。
- CopyOnWrite思想:平时查询时,不加锁,更新时从原来的数据copy副本,然后修改副本,最后把原数据替换为副本。修改时,不阻塞读操作,读到的是旧数据。
- 优缺点
- 优点:对于读多写少的场景, CopyOnWrite这种无锁操作性能更好,相比于其它同步容器
- 缺点:①数据一致性问题,②内存占用问题及导致更多的GC次数
ConcurrentHashMap
并发队列
队列是线程协作的利器,通过队列可以很容易的实现数据共享,并且解决上下游处理速度不匹配的问题,典型的生产者消费者模式
什么是阻塞队列
- 带阻塞能力的队列,阻塞队列一端是给生产者put数据使用,另一端给消费者take数据使用
- 阻塞队列是线程安全的,生产者和消费者都可以是多线程
- take方法:获取并移除头元素,如果队列无数据,则阻塞
- put方法:插入元素,如果队列已满,则阻塞
- 阻塞队列又分为有界和无界队列,无界队列不是无限队列,最大值Integer.MAX_VALU
常用阻塞队列
- ArrayBlockingQueue 基于数组实现的有界阻塞队列
- LinkedBlockingQueue 基于链表实现的无界阻塞队列
- SynchronousQueue不存储元素的阻塞队列
- PriorityBlockingQueue 支持按优先级排序的无界阻塞队列
- DelayQueue优先级队列实现的双向无界阻塞队列
- LinkedTransferQueue基于链表实现的无界阻塞队列
- LinkedBlockingDeque基于链表实现的双向无界阻塞队列
ThreadLocal
ThreadLocal是线程本地变量类,在多线程并执行过程中,将变量存储在ThreadLocal中,每个线程中都有独立的变量,因此不会出现线程安全问题。
ThreadLocal 是解决线程安全问题一个较好方案,它通过为每个线程提供一个独立的本地值,去解决并发访问的冲突问题。很多情况下,使用 ThreadLocal 比直接使用同步机制(如 synchronized)解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性。
- ThreadLocal在Spring中作用巨大,在管理Request作用域中的Bean、事务、任务调度、AOP等模块都有它。
- Spring中绝大部分Bean都可以声明成Singleton作用域,采用ThreadLocal进行封装,因此有状态的Bean就能够以singleton的方式在多线程中正常工作了。
ThreadLocal底层原理
JDK1.8之前:ThreadLocal是Map所有线程拥有同一个,Key为thread,Value为具体值 JDK1.8:ThreadLocal依旧是Map,但一个线程一个ThreadLocalMap,key为ThreadLocal,Value为具体值 主要变化:①ThreadLocalMap的拥有者,②Key
JDK1.8中Thread、ThreadLocal、ThreadLocalMap的关系?
➢ Thread –> ThreadLocalMap –> Entry(ThreadLocalN, LocalValueN)*n