Java并发编程核心知识详解
本文系统梳理Java并发编程的核心知识点包括并发三大特性、锁机制、线程池、ThreadLocal等适合面试复习和日常开发参考。一、并发编程的三大特性特性说明保障手段原子性操作不可被中断要么全部执行完要么不执行synchronized、Lock、CAS可见性一个线程对共享变量的修改对其他线程立即可见volatile关键字有序性程序按代码顺序执行禁止指令重排volatile、happens-before规则二、volatile关键字保证数据的可见性每次使用都到主存中读取不能保证数据的原子性防止指令重排序适用场景状态标志位、双重检查锁定中的单例变量。三、乐观锁与悲观锁乐观锁实现方式版本号机制或CAS算法CAS算法Compare And Swap原子操作底层依赖CPU原子指令。包含三个操作数V内存值、E期望值、N新值当V和E相等时才会用N更新V。CAS的问题ABA问题可通过版本号AtomicStampedReference解决自旋开销CAS失败后不断重试CPU空转只能保证单个变量的原子操作四、synchronized4.1 是什么Java内置Monitor锁JVM层面的关键字。4.2 作用原子性同一时刻只有一个线程执行可见性锁释放前对共享变量的修改对其他线程可见有序性防止指令重排序4.3 底层原理修饰代码块monitorenter和monitorexit修饰方法ACC_SYNCHRONIZED本质都是对对象监视器monitor的获取4.4 锁升级过程不可逆无锁 → 偏向锁 → 轻量级锁 → 重量级锁锁状态触发时机适用场景偏向锁首次被线程获取CAS记录线程ID单线程反复进入同步块轻量级锁其他线程尝试获取锁多线程交替执行短时间同步重量级锁自旋超过阈值默认10次竞争激烈场景4.5 synchronized vs ReentrantLock维度synchronizedReentrantLock是什么JVM内置关键字JDK并发包提供的AQS实现类获取/释放JVM隐式管理手动try-finally释放公平性非公平锁可配置公平/非公平等待可中断不可中断支持lockInterruptibly()锁绑定条件不能绑定可绑定多个Condition尝试加锁不能尝试tryLock()立即返回五、AQS抽象队列同步器5.1 是什么Doug Lea设计的抽象队列同步器是JDK并发包的核心框架。5.2 设计原理通过state变量管理锁的获取与释放FIFO队列管理等待线程的排队与唤醒两种模式独占模式ReentrantLock和共享模式Semaphore, CountDownLatch5.3 公平锁 vs 非公平锁维度公平锁非公平锁等待队列FIFO队列严格按顺序抢占式可能插队hasQueuedPredecessor()必须检查无前驱才获取不检查吞吐量较低较高减少线程阻塞唤醒响应及时性可能被短等待抢占可能长时间饥饿六、线程池 ThreadPoolExecutor6.1 七大参数ThreadPoolExecutor( int corePoolSize, // 核心线程数 int maximumPoolSize, // 最大线程数 long keepAliveTime, // 空闲线程存活时间 TimeUnit unit, // 时间单位 BlockingQueueRunnable workQueue, // 任务队列 ThreadFactory threadFactory, // 线程工厂 RejectedExecutionHandler handler // 拒绝策略 )6.2 执行流程poolSize corePoolSize → 创建核心线程poolSize corePoolSize → 任务进入队列等待队列满且poolSize maximumPoolSize → 创建临时线程都满 → 执行拒绝策略6.3 四种拒绝策略策略行为AbortPolicy抛出RejectedExecutionException默认CallerRunsPolicy由调用线程执行任务DiscardPolicy直接丢弃任务DiscardOldestPolicy丢弃队列最旧任务6.4 为什么禁止使用Executors创建线程池FixedThreadPool/SingleThreadPool队列容量为Integer.MAX_VALUE可能导致OOMCachedThreadPoolmaximumPoolSize为Integer.MAX_VALUE可能创建过多线程正确做法使用new ThreadPoolExecutor手动设置合理参数6.5 核心线程数设置CPU密集型核心线程数 CPU核数 1IO密集型核心线程数 CPU核数 * 26.6 线程复用原理线程池里的线程被封装成了 Worker 对象。Worker 启动后进入一个死循环While Loop不断地调用getTask()方法从阻塞队列中获取任务。如果队列为空核心线程会调用take()方法阻塞挂起非核心线程会调用poll(time)进行超时等待。如果在 keepAliveTime 内没拿到任务循环结束线程销毁。七、ThreadLocal7.1 是什么Java提供的线程局部变量工具类使每个线程都拥有自己的独立副本。7.2 原理底层实现依赖ThreadLocalMap存储以ThreadLocal为key、Object对象为value的键值对。7.3 使用场景用户会话管理、数据库连接管理、线程上下文信息。7.4 内存泄漏问题key是弱引用value是强引用ThreadLocal实例失去强引用后value仍存在于ThreadLocalMap中解决使用完之后调用remove()7.5 为什么key要设计为弱引用如果key是强引用ThreadLocal实例无法被GC回收造成key和value都无法回收的内存泄漏弱引用允许ThreadLocal被GC回收但value仍可能泄漏需要手动remove八、死锁产生死锁的四个必要条件互斥条件请求和保持条件不剥夺条件循环等待条件预防死锁破坏请求和保持条件、破坏不剥夺条件、破坏循环等待条件避免死锁银行家算法对资源分配进行计算评估使其进入安全状态总结Java并发编程的核心在于理解线程安全的本质在多线程环境下保证共享数据的正确性。掌握volatile、synchronized、AQS、线程池这几个核心知识点基本能覆盖大部分面试和实际开发场景。