一、JUCJUC 是java.util.concurrent包的缩写是 Java 并发编程的核心工具包。它从 JDK 1.5 开始引入为解决多线程并发问题提供了强大的组件。1.1、JUC核心架构java.util.concurrent ├── atomic// 原子类├── locks// 锁框架├── collections// 并发集合├── executors// 线程池框架└── tools// 同步工具1.2、原子类核心原理CAS// CAS 伪代码booleancompareAndSwap(V,A,B):if(VA):VBreturntruereturnfalse原子类分类使用示例// AtomicInteger 基本用法AtomicIntegercounternewAtomicInteger(0);counter.incrementAndGet();// icounter.getAndIncrement();// i// LongAdder - 适合高并发写多读少的场景LongAdderaddernewLongAdder();adder.increment();longsumadder.sum();// 解决 ABA 问题 - 带版本号的原子引用AtomicStampedReferenceStringrefnewAtomicStampedReference(A,0);1.3、锁框架ReentrantLock定义可重入互斥锁和synchronized定位类似都是用来实现互斥效果保证线程安全。ReentrantLock的用法lock()加锁如果获取不到锁就死等。trylock(超时时间)加锁如果获取不到锁等待一定的时间之后就放弃加锁。unlock()解锁ReentrantLock和synchronized的区别synchronized是一个关键字是JVM内部实现的(大概率是基于C实现)ReentrantLock是标准库的一个类在JVM外实现的(基于Java实现)。synchronized使用时不需要手动释放锁。ReentrantLock使用时需要手动释放。使用起来更灵活但是也容易遗漏unlock。synchronized在申请锁失败时会死等。ReentrantLock可以通过trylock的方式等待一段时间就放弃。synchronized是非公平锁ReentrantLock默认是非公平锁.可以通过构造方法传入一个true开启公平锁模式。更强大的唤醒机制.。synchronized是通过Object的wait/notify实现等待-唤醒。每次唤醒的是一个随机等待的线程。ReentrantLock搭配Condition类实现等待-唤醒可以更精确控制唤醒某个指定的线程.如何选择使用哪个锁?锁竞争不激烈的时候使用synchronized效率更高自动释放更方便。锁竞争激烈的时候使用ReentrantLock搭配trylock更灵活控制加锁的行为而不是死等。如果需要使用公平锁使用ReentrantLock。1.4、线程池核心接口Executor(顶层接口)├──ExecutorService(核心接口)│ ├──ThreadPoolExecutor// 最常用│ └──ScheduledExecutorService│ └──ScheduledThreadPoolExecutor└──ForkJoinPool(分治框架)ThreadPoolExecutor 七大参数publicThreadPoolExecutor(intcorePoolSize,// 核心线程数intmaximumPoolSize,// 最大线程数longkeepAliveTime,// 空闲线程存活时间TimeUnitunit,// 时间单位BlockingQueueRunnableworkQueue,// 任务队列ThreadFactorythreadFactory,// 线程工厂RejectedExecutionHandlerhandler// 拒绝策略)任务执行流程提交任务 ↓ 核心线程是否已满 ──否──→ 创建核心线程执行 ↓是 任务队列是否已满 ──否──→ 加入队列等待 ↓是 最大线程是否已满 ──否──→ 创建非核心线程执行 ↓是 执行拒绝策略内置线程池拒绝策略// 四种内置策略AbortPolicy// 抛异常默认CallerRunsPolicy// 调用者线程执行DiscardPolicy// 丢弃不抛异常DiscardOldestPolicy// 丢弃队列头重提任务1.5、Semaphore信号量信号量用来表示“可用资源的个数”。本质上就是⼀个计数器。理解信号量可以把信号量想象成是停车场的展示牌当前有车位100个。表示有100个可用资源。当有车开进去的时候就相当于申请一个可用资源可用车位就-1(这个称为信号量的P操作)。当有车开出来的时候就相当于释放一个可用资源可用车位就1(这个称为信号量的V操作)。如果计数器的值已经为0了还尝试申请资源就会阻塞等待直到有其他线程释放资源。Semaphore的PV操作中的加减计数器操作都是原子的可以在多线程环境下直接使用。代码示例// 场景限流控制并发数SemaphoresemaphorenewSemaphore(5);// 最多5个线程同时访问voidaccess(){semaphore.acquire();// 获取许可try{// 业务代码}finally{semaphore.release();// 释放许可}}二、线程安全的集合类在 Java 中线程安全的集合主要分为三类早期的线程安全集合、同步包装器、JUC 并发集合。2.1、早期线程安全集合代码示例// 示例Vector 线程安全但性能差VectorStringvectornewVector();vector.add(a);// 内部 synchronizedStringsvector.get(0);缺点所有方法都加同一把锁对象锁并发度极低即使读操作也互斥。2.2、同步包装器Collections.synchronizedXXX()方法可以将普通集合包装成线程安全集合。特点基于synchronized代码块锁对象可指定默认是包装后的集合对象。迭代器不是线程安全的遍历时必须加锁。性能依然较差因为所有操作都串行化。2.3、JUC 并发集合java.util.concurrent 包现代 Java 并发编程的首选提供了细粒度锁、无锁算法等高效实现。2.3.1、ConcurrentHashMap线程安全的哈希表JDK 1.8 采用 CAS synchronized锁头结点并发度极高。特点不允许 null 键/值。迭代器是弱一致性的不抛出ConcurrentModificationException但可能不反映最新修改。适用于高并发读写。2.3.2、CopyOnWriteArrayList / CopyOnWriteArraySet写时复制思想写操作复制一个新数组修改完成后替换原数组读操作在原数组上进行无需加锁。特点读多写少场景极佳如配置信息、监听器列表。写操作开销大数组复制不适合写频繁的场景。迭代器是快照风格不会抛出ConcurrentModificationException。2.3.3、ConcurrentLinkedQueue / ConcurrentLinkedDeque无界非阻塞队列基于 CAS 实现。特点无锁、高吞吐。不阻塞适合生产者-消费者模型但不需要阻塞等待的场景。迭代器弱一致性。2.3.4、BlockingQueue 接口及其实现阻塞队列当队列满时put()阻塞当队列空时take()阻塞。常用于生产者-消费者模式。2.3.5、ConcurrentSkipListMap / ConcurrentSkipListSet基于跳表的并发有序Map/Set类似于TreeMap的并发版本。特点线程安全且有序。无锁CAS并发性能优于Collections.synchronizedSortedMap。提供lowerKeyceilingKey等导航方法。三、死锁死锁是并发编程中的一种严重问题两个或更多线程互相持有对方需要的资源且都不释放自己占有的资源导致所有线程都无法继续执行。3.1、死锁的四个必要条件四个条件必须同时满足才会发生死锁互斥条件资源一次只能被一个线程占用。请求与保持条件线程持有至少一个资源同时又在请求其他资源。不可剥夺条件线程已获得的资源在使用完之前不能被其他线程强行剥夺。循环等待条件存在一个线程等待环例如 T1 等待 T2 的资源T2 等待 T3 的资源…Tn 等待 T1 的资源。3.2、死锁的避免与预防破坏“请求与保持”条件一次性申请所有资源破坏“不可剥夺”条件使用可超时的锁破坏“循环等待”条件按固定顺序获取锁使用高级并发工具使用ConcurrentHashMap、BlockingQueue等避免显式加锁使用Semaphore、CountDownLatch等减少锁竞争。避免嵌套锁尽量减少同步块内调用外部方法可能持有其他锁能不用多个锁就不用。