线程安全、读操做无锁的ArrayList。html
基本思想:当咱们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,而后新的容器里添加元素,添加完元素以后,再将原容器的引用指向新的容器。这样作的好处是咱们能够对CopyOnWrite容器进行并发的读,而不须要加锁,由于当前容器不会添加任何元素。因此CopyOnWrite容器也是一种读写分离的思想,读和写不一样的容器。java
CopyOnWrite并发容器用于读多写少的并发场景。缺点:(1)内存占用问题;(2)只能保证数据的最终一致性,不能保证数据的实时一致性。面试
深刻分析可参考:并发编程网-聊聊并发-Java中的Copy-On-Write容器编程
基于CopyOnWriteArrayList实现,原理相似,add操做要遍历数组以免添加剧复元素。数组
等待多线程完成的CountDownLatch,实现异步转同步操做。安全
深刻分析可参考:并发编程网-并发工具类(一)等待多线程完成的CountDownLatch多线程
比CountDownLatch更强大,当await的数量到达指定数量后,才继续往下执行。通俗点讲就是:让一组线程到达一个屏障时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,全部被屏障拦截的线程才会继续运行。多用于多线程计算数据,最后合并计算结果的场景。并发
深刻分析可参考:程序猿DD-死磕Java并发:J.U.C之并发工具类:CyclicBarrier异步
控制某资源被同时访问个数,是对锁的扩展,指定多个线程访问某一资源。对于临界区管理代码,程序会限制同时执行这段代码的线程数。ide
import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; /** * Semaphore实现限流案例 */ public class Main { /** * 申请信号量准入数,即同时能申请多少个许可 */ static Semaphore semaphore = new Semaphore(10); public static void main(String[] args) throws Exception { final Executor executor = Executors.newFixedThreadPool(1000); for (int i = 0; i < 2000; i++) { executor.execute(new Runnable() { @Override public void run() { executor(); } }); } } public static void executor() { try { if (semaphore.getQueueLength() > 10) { System.out.println("wait..."); return; } /** * 尝试得到一个许可,若存在可用资源,直接返回 * 不然进入等待队列,不断尝试得到资源,而tryAcquire不会等待 */ semaphore.acquire(); // 模拟耗时的业务逻辑 Thread.sleep(1000); System.out.println("run..."); } catch (InterruptedException e) { e.printStackTrace(); } finally { // 将信号量释放,以便其余请求能够得到空闲资源 semaphore.release(); } } }
交换者是用于线程间协做的工具类,用于线程间的数据交换,它提供一个同步点,在这个同步点,两个线程能够交换彼此的数据。
深刻分析能够参考:芋道源码-【死磕 Java 并发】—– J.U.C 之并发工具类:Exchanger
五月的仓颉-Java多线程21:多线程下的其余组件之CyclicBarrier、Callable、Future和FutureTask
归纳起来讲,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不一样的线程排队访问,然后者为每个线程都提供了一份变量,所以能够同时访问而互不影响。
程序猿DD-「图解」ThreadLocal 在并发问题中的应用(原理剖析、InheritableThreadLocal)
占小狼的博客-面试官再问你ThreadLocal,你就这样“怼”回去(使用场景及最佳实践)