Java线程与并发编程核心原理与实践

Java线程与并发编程核心原理与实践
1. Java线程基础概念解析线程作为Java并发编程的核心概念理解其本质是掌握多线程编程的第一步。线程是操作系统能够进行运算调度的最小单位它被包含在进程之中是进程中的实际运作单位。在Java中每个线程都对应一个Thread类的实例。1.1 线程与进程的区别线程和进程是现代操作系统的两个基本概念它们的主要区别体现在以下几个方面资源分配进程是操作系统资源分配的基本单位而线程是处理器任务调度和执行的基本单位开销差异进程拥有独立的代码和数据空间切换开销大线程共享进程资源切换开销小内存共享同一进程的线程共享地址空间和资源进程间资源相互独立健壮性一个线程崩溃会导致整个进程终止而进程崩溃不会影响其他进程// 进程与线程的关系示例 public class ProcessAndThread { public static void main(String[] args) { // 主线程进程中的第一个线程 System.out.println(Main thread: Thread.currentThread().getName()); // 创建新线程 new Thread(() - { System.out.println(New thread: Thread.currentThread().getName()); }).start(); } }1.2 线程的生命周期Java线程在其生命周期中会经历多种状态新建(NEW)线程对象刚被创建但尚未启动就绪(RUNNABLE)调用start()后线程等待CPU时间片运行(RUNNING)线程获得CPU时间片执行run()方法阻塞(BLOCKED)线程暂时放弃CPU使用权终止(TERMINATED)线程执行完毕或异常退出// 线程状态观察示例 public class ThreadStateDemo { public static void main(String[] args) throws InterruptedException { Thread thread new Thread(() - { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } }); System.out.println(Before start: thread.getState()); // NEW thread.start(); System.out.println(After start: thread.getState()); // RUNNABLE Thread.sleep(500); System.out.println(During sleep: thread.getState()); // TIMED_WAITING thread.join(); System.out.println(After finish: thread.getState()); // TERMINATED } }2. Java线程创建与管理2.1 线程创建的三种方式Java提供了多种创建线程的方式每种方式都有其适用场景继承Thread类class MyThread extends Thread { Override public void run() { System.out.println(Thread running by extending Thread); } } // 使用 new MyThread().start();实现Runnable接口class MyRunnable implements Runnable { Override public void run() { System.out.println(Thread running by implementing Runnable); } } // 使用 new Thread(new MyRunnable()).start();实现Callable接口带返回值class MyCallable implements CallableString { Override public String call() throws Exception { return Result from Callable; } } // 使用 ExecutorService executor Executors.newSingleThreadExecutor(); FutureString future executor.submit(new MyCallable()); System.out.println(future.get()); // 获取返回值 executor.shutdown();提示实际开发中推荐使用Runnable或Callable接口方式因为Java不支持多重继承使用接口更灵活。2.2 线程的基本操作2.2.1 线程启动与中断正确启动线程应该调用start()方法而非直接调用run()方法。start()会创建新线程执行run()方法而直接调用run()会在当前线程执行。// 正确的中断线程方式 public class ThreadInterruptDemo { public static void main(String[] args) throws InterruptedException { Thread thread new Thread(() - { while (!Thread.currentThread().isInterrupted()) { System.out.println(Thread is running...); try { Thread.sleep(1000); } catch (InterruptedException e) { System.out.println(Thread interrupted during sleep); Thread.currentThread().interrupt(); // 重新设置中断标志 } } System.out.println(Thread exiting...); }); thread.start(); Thread.sleep(3000); thread.interrupt(); // 中断线程 } }2.2.2 线程等待与通知wait()/notify()机制是实现线程间通信的基础public class WaitNotifyDemo { private static final Object lock new Object(); private static boolean condition false; public static void main(String[] args) { new Thread(() - { synchronized (lock) { while (!condition) { try { System.out.println(Thread waiting...); lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread notified, condition met); } }).start(); new Thread(() - { synchronized (lock) { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } condition true; lock.notify(); System.out.println(Notified waiting thread); } }).start(); } }3. 线程同步与锁机制3.1 synchronized关键字synchronized是Java内置的同步机制可以修饰方法或代码块// 同步方法 public synchronized void syncMethod() { // 临界区代码 } // 同步代码块 public void syncBlock() { synchronized(this) { // 临界区代码 } }synchronized的实现原理同步代码块使用monitorenter和monitorexit指令同步方法使用ACC_SYNCHRONIZED标志依赖于对象头中的Mark Word实现锁状态记录3.2 ReentrantLockReentrantLock是JDK提供的更灵活的锁实现public class ReentrantLockDemo { private final ReentrantLock lock new ReentrantLock(); public void performAction() { lock.lock(); // 获取锁 try { // 临界区代码 } finally { lock.unlock(); // 确保锁释放 } } }ReentrantLock相比synchronized的优势可中断的锁获取公平锁与非公平锁选择多个条件变量支持尝试获取锁(tryLock)3.3 volatile关键字volatile保证变量的可见性和有序性public class VolatileDemo { private volatile boolean flag false; public void writer() { flag true; // 写操作 } public void reader() { if (flag) { // 读操作 // 基于flag的操作 } } }volatile适用场景状态标志位单例模式的双重检查锁定独立观察独立于程序状态的状态标志4. 线程池原理与实践4.1 线程池核心参数ThreadPoolExecutor的构造参数public ThreadPoolExecutor( int corePoolSize, // 核心线程数 int maximumPoolSize, // 最大线程数 long keepAliveTime, // 空闲线程存活时间 TimeUnit unit, // 时间单位 BlockingQueueRunnable workQueue, // 工作队列 ThreadFactory threadFactory, // 线程工厂 RejectedExecutionHandler handler // 拒绝策略 )4.2 线程池工作流程提交任务后如果当前线程数 corePoolSize创建新线程执行任务如果线程数 ≥ corePoolSize将任务放入工作队列如果队列已满且线程数 maximumPoolSize创建新线程执行任务如果队列已满且线程数 ≥ maximumPoolSize执行拒绝策略4.3 常见线程池类型FixedThreadPool固定大小线程池ExecutorService fixedPool Executors.newFixedThreadPool(5);CachedThreadPool可缓存线程池ExecutorService cachedPool Executors.newCachedThreadPool();ScheduledThreadPool定时任务线程池ScheduledExecutorService scheduledPool Executors.newScheduledThreadPool(3);SingleThreadExecutor单线程池ExecutorService singleThreadPool Executors.newSingleThreadExecutor();注意阿里巴巴Java开发手册建议手动创建ThreadPoolExecutor避免使用Executors工厂方法因为后者可能创建无界队列导致OOM。4.4 线程池最佳实践// 自定义线程池示例 public class CustomThreadPool { private static final int CORE_POOL_SIZE Runtime.getRuntime().availableProcessors(); private static final int MAX_POOL_SIZE CORE_POOL_SIZE * 2; private static final long KEEP_ALIVE_TIME 1L; private static final int QUEUE_CAPACITY 100; public static ExecutorService createCustomThreadPool() { return new ThreadPoolExecutor( CORE_POOL_SIZE, MAX_POOL_SIZE, KEEP_ALIVE_TIME, TimeUnit.MINUTES, new LinkedBlockingQueue(QUEUE_CAPACITY), new ThreadPoolExecutor.CallerRunsPolicy() ); } }5. 高级并发工具类5.1 CountDownLatch允许一个或多个线程等待其他线程完成操作public class CountDownLatchDemo { public static void main(String[] args) throws InterruptedException { int threadCount 5; CountDownLatch latch new CountDownLatch(threadCount); for (int i 0; i threadCount; i) { new Thread(() - { System.out.println(Thread.currentThread().getName() working); latch.countDown(); }).start(); } latch.await(); System.out.println(All threads completed); } }5.2 CyclicBarrier让一组线程到达屏障时被阻塞直到最后一个线程到达public class CyclicBarrierDemo { public static void main(String[] args) { int threadCount 3; CyclicBarrier barrier new CyclicBarrier(threadCount, () - System.out.println(All threads reached barrier)); for (int i 0; i threadCount; i) { new Thread(() - { try { System.out.println(Thread.currentThread().getName() waiting); barrier.await(); System.out.println(Thread.currentThread().getName() continued); } catch (Exception e) { e.printStackTrace(); } }).start(); } } }5.3 Semaphore控制同时访问特定资源的线程数量public class SemaphoreDemo { public static void main(String[] args) { int permits 3; Semaphore semaphore new Semaphore(permits); for (int i 0; i 10; i) { new Thread(() - { try { semaphore.acquire(); System.out.println(Thread.currentThread().getName() acquired permit); Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } finally { semaphore.release(); System.out.println(Thread.currentThread().getName() released permit); } }).start(); } } }6. 线程安全与性能优化6.1 线程安全集合Java并发包提供了多种线程安全集合ConcurrentHashMap分段锁实现的线程安全HashMapCopyOnWriteArrayList写时复制的线程安全ListConcurrentLinkedQueue无锁实现的线程安全队列BlockingQueue接口实现类ArrayBlockingQueue、LinkedBlockingQueue等// ConcurrentHashMap使用示例 public class ConcurrentHashMapDemo { public static void main(String[] args) { ConcurrentHashMapString, Integer map new ConcurrentHashMap(); // 线程安全的putIfAbsent map.putIfAbsent(key, 1); // 原子更新 map.compute(key, (k, v) - v null ? 1 : v 1); } }6.2 ThreadLocal原理与应用ThreadLocal为每个线程提供独立的变量副本public class ThreadLocalDemo { private static final ThreadLocalSimpleDateFormat dateFormatHolder ThreadLocal.withInitial(() - new SimpleDateFormat(yyyy-MM-dd)); public static void main(String[] args) { ExecutorService executor Executors.newFixedThreadPool(5); for (int i 0; i 10; i) { executor.execute(() - { SimpleDateFormat sdf dateFormatHolder.get(); System.out.println(Thread.currentThread().getName() using: sdf.format(new Date())); }); } executor.shutdown(); } }注意使用ThreadLocal后必须调用remove()方法清理避免内存泄漏特别是在线程池环境中。6.3 避免死锁的策略锁顺序所有线程以相同顺序获取锁锁超时尝试获取锁时设置超时时间死锁检测定期检查死锁并恢复避免嵌套锁尽量减少锁的嵌套使用// 锁顺序示例 public class LockOrderingDemo { private final Object lock1 new Object(); private final Object lock2 new Object(); public void method1() { synchronized(lock1) { synchronized(lock2) { // 临界区 } } } public void method2() { synchronized(lock1) { // 与method1相同的锁顺序 synchronized(lock2) { // 临界区 } } } }7. Java内存模型与happens-before7.1 Java内存模型(JMM)JMM定义了线程如何与内存交互主要关注原子性基本类型读写是原子的long/double除外可见性一个线程的修改对其他线程可见有序性指令重排序的限制7.2 happens-before原则程序顺序规则同一线程中的操作按程序顺序执行锁规则解锁操作先于后续的加锁操作volatile规则volatile写操作先于后续的读操作线程启动规则Thread.start()先于线程中的任何操作线程终止规则线程中的所有操作先于线程终止检测中断规则interrupt()调用先于检测到中断终结器规则对象构造先于finalize()方法传递性A happens-before BB happens-before C则A happens-before C// happens-before示例 public class HappensBeforeDemo { private int x 0; private volatile boolean v false; public void writer() { x 42; // 1 v true; // 2 } public void reader() { if (v) { // 3 System.out.println(x); // 保证看到42 } } }8. 并发编程实践与性能调优8.1 并发设计模式生产者-消费者模式public class ProducerConsumer { private final BlockingQueueInteger queue new LinkedBlockingQueue(10); class Producer implements Runnable { public void run() { try { while (true) { queue.put(1); Thread.sleep(100); } } catch (InterruptedException e) { e.printStackTrace(); } } } class Consumer implements Runnable { public void run() { try { while (true) { Integer item queue.take(); System.out.println(Consumed: item); } } catch (InterruptedException e) { e.printStackTrace(); } } } }读写锁模式public class ReadWriteLockDemo { private final ReentrantReadWriteLock rwLock new ReentrantReadWriteLock(); private final Lock readLock rwLock.readLock(); private final Lock writeLock rwLock.writeLock(); private MapString, String data new HashMap(); public String get(String key) { readLock.lock(); try { return data.get(key); } finally { readLock.unlock(); } } public void put(String key, String value) { writeLock.lock(); try { data.put(key, value); } finally { writeLock.unlock(); } } }8.2 性能优化建议减少锁粒度缩小同步代码块范围减少锁持有时间尽快释放锁锁分离读写锁分离无锁编程使用CAS操作避免热点字段如AtomicLong可能成为性能瓶颈合理设置线程池参数根据任务类型调整// 锁粒度优化示例 public class LockGranularity { private final MapString, String map1 new HashMap(); private final MapString, String map2 new HashMap(); private final Object lock1 new Object(); private final Object lock2 new Object(); // 细粒度锁 public void put1(String key, String value) { synchronized(lock1) { map1.put(key, value); } } public void put2(String key, String value) { synchronized(lock2) { map2.put(key, value); } } }9. Java并发工具进阶9.1 CompletableFutureJava 8引入的异步编程工具public class CompletableFutureDemo { public static void main(String[] args) throws Exception { CompletableFutureString future CompletableFuture.supplyAsync(() - { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } return Hello; }).thenApplyAsync(s - s World) .thenApply(String::toUpperCase); System.out.println(future.get()); // 输出HELLO WORLD } }9.2 Fork/Join框架适用于分治算法的并行框架public class ForkJoinDemo { static class FibonacciTask extends RecursiveTaskLong { final long n; FibonacciTask(long n) { this.n n; } protected Long compute() { if (n 1) return n; FibonacciTask f1 new FibonacciTask(n - 1); f1.fork(); FibonacciTask f2 new FibonacciTask(n - 2); return f2.compute() f1.join(); } } public static void main(String[] args) { ForkJoinPool pool new ForkJoinPool(); FibonacciTask task new FibonacciTask(10); System.out.println(pool.invoke(task)); // 输出55 } }10. 常见问题排查与调试10.1 线程转储分析获取线程转储的方法jstack命令jstack pid通过JMXThreadMXBean.dumpAllThreads()发送信号kill -3 pidLinux分析线程转储关注点死锁线程标记为BLOCKED长时间运行的线程大量等待同一锁的线程线程状态分布10.2 常见并发问题死锁使用jstack检测活锁线程不断改变状态但无法继续饥饿低优先级线程长期得不到执行竞态条件结果依赖于线程执行顺序内存一致性错误违反happens-before规则10.3 调试技巧使用ThreadLocalRandom替代Random减少争用避免在锁内调用外部方法防止死锁使用并发集合替代同步包装类合理设置线程池大小CPU密集型CPU核数1IO密集型CPU核数*2// 线程池大小计算示例 public class PoolSizeCalculator { public static int getIdealThreadPoolSize() { int cores Runtime.getRuntime().availableProcessors(); // IO密集型任务 return (int)(cores / (1 - 0.9)); // 假设阻塞系数为0.9 } }11. Java并发新特性11.1 Java虚拟线程Project LoomJava 19引入的轻量级线程// 虚拟线程使用示例 public class VirtualThreadDemo { public static void main(String[] args) throws Exception { try (var executor Executors.newVirtualThreadPerTaskExecutor()) { for (int i 0; i 10_000; i) { executor.submit(() - { Thread.sleep(Duration.ofSeconds(1)); return Done; }); } } } }虚拟线程特点由JVM管理非操作系统线程创建成本极低可创建数百万个阻塞操作不会占用系统线程11.2 结构化并发Java 21简化多线程编程模型// 结构化并发示例 public class StructuredConcurrencyDemo { public static void main(String[] args) { try (var scope new StructuredTaskScope.ShutdownOnFailure()) { FutureString user scope.fork(() - findUser()); FutureInteger order scope.fork(() - fetchOrder()); scope.join(); // 等待所有任务 scope.throwIfFailed(); // 检查异常 System.out.println(User: user.resultNow() , Order: order.resultNow()); } catch (Exception e) { e.printStackTrace(); } } }12. 并发编程最佳实践优先使用高级并发工具如并发集合、线程池避免过早优化先保证正确性再考虑性能编写无状态对象减少同步需求使用不可变对象简化线程安全设计文档化线程安全策略明确类的线程安全级别避免过度同步缩小同步范围考虑替代方案如消息传递、Actor模型全面测试并发代码包括压力测试和边界测试// 不可变对象示例 public final class ImmutablePoint { private final int x; private final int y; public ImmutablePoint(int x, int y) { this.x x; this.y y; } public int getX() { return x; } public int getY() { return y; } public ImmutablePoint move(int dx, int dy) { return new ImmutablePoint(x dx, y dy); } }13. 常见面试问题解析13.1 synchronized与ReentrantLock区别特性synchronizedReentrantLock实现机制JVM内置实现JDK代码实现锁获取方式自动获取释放需要手动lock/unlock可中断性不支持支持lockInterruptibly()公平锁非公平可选择公平/非公平条件变量单个等待队列支持多个Condition性能JDK6后优化性能接近提供更丰富的功能13.2 volatile与synchronized区别特性volatilesynchronized作用范围变量级别变量、方法、代码块级别原子性不保证复合操作原子性保证操作的原子性可见性保证可见性保证可见性有序性禁止指令重排序保证有序性线程阻塞不会导致线程阻塞可能导致线程阻塞适用场景状态标志、双重检查锁定需要原子性操作的场景13.3 线程池参数配置建议CPU密集型任务corePoolSize CPU核数 1maximumPoolSize corePoolSize使用有界队列防止资源耗尽IO密集型任务corePoolSize CPU核数 × 2maximumPoolSize corePoolSize × 2使用较大的队列缓冲混合型任务将任务分类使用不同的线程池处理监控调整参数达到最佳性能// 动态调整线程池参数示例 public class DynamicThreadPool extends ThreadPoolExecutor { public DynamicThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueueRunnable workQueue) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue); } // 动态调整核心线程数 public void setCorePoolSize(int corePoolSize) { super.setCorePoolSize(corePoolSize); } // 动态调整最大线程数 public void setMaximumPoolSize(int maximumPoolSize) { super.setMaximumPoolSize(maximumPoolSize); } }14. 并发编程的未来趋势响应式编程如Reactor、RxJava协程/虚拟线程轻量级并发模型无锁数据结构提高并发性能函数式并发不可变数据纯函数分布式并发跨JVM的协调机制硬件感知并发利用NUMA架构特性// 响应式编程示例 public class ReactiveExample { public static void main(String[] args) { Flux.range(1, 10) .parallel() .runOn(Schedulers.parallel()) .map(i - i * 2) .subscribe(System.out::println); } }15. 实战经验分享在实际项目中应用多线程时我总结了以下几点经验明确并发需求不是所有场景都需要并发评估真正需要并发的部分优先使用现有工具如并发集合、线程池避免重复造轮子保持简单复杂的并发设计难以维护和调试全面测试包括功能测试、性能测试和压力测试监控与调优使用JMX、Profiler等工具持续监控渐进式优化先保证正确性再逐步优化性能文档与注释详细记录线程安全策略和并发设计// 实际项目中的线程安全计数器 public class SafeCounter { private final AtomicLong counter new AtomicLong(); private final LongAdder fastCounter new LongAdder(); // 精确计数 public long incrementAndGet() { return counter.incrementAndGet(); } // 高性能计数 public void fastIncrement() { fastCounter.increment(); } public long fastSum() { return fastCounter.sum(); } }16. 性能对比与基准测试不同并发工具的性能差异场景实现方式吞吐量(ops/ms)备注计数器synchronized1,200高争用时性能下降计数器AtomicLong5,800CAS实现计数器LongAdder12,400低争用下最优Map操作Hashtable850全表锁Map操作Collections.synchronizedMap920包装器模式Map操作ConcurrentHashMap7,300分段锁基准测试建议使用JMH(Java Microbenchmark Harness)进行可靠的微基准测试避免JVM优化带来的误差。// JMH基准测试示例 BenchmarkMode(Mode.Throughput) OutputTimeUnit(TimeUnit.MILLISECONDS) public class CounterBenchmark { State(Scope.Thread) public static class MyState { public final AtomicLong atomicCounter new AtomicLong(); public final LongAdder adderCounter new LongAdder(); } Benchmark public long testAtomicIncrement(MyState state) { return state.atomicCounter.incrementAndGet(); } Benchmark public void testAdderIncrement(MyState state) { state.adderCounter.increment(); } }17. 并发设计模式进阶17.1 Thread-Per-Message模式为每个请求创建独立线程处理public class ThreadPerMessageDemo { public static void main(String[] args) { Host host new Host(); host.request(10, A); host.request(20, B); } static class Host { private final ExecutorService executor Executors.newCachedThreadPool(); public void request(int count, char c) { executor.execute(() - { for (int i 0; i count; i) { System.out.print(c); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } }); } } }17.2 Worker Thread模式使用工作线程池处理任务public class WorkerThreadDemo { public static void main(String[] args) { Channel channel new Channel(5); channel.startWorkers(); new ClientThread(Alice, channel).start(); new ClientThread(Bobby, channel).start(); } static class Channel { private final BlockingQueueRequest queue; private final Thread[] threads; public Channel(int threads) { this.queue new LinkedBlockingQueue(); this.threads new Thread[threads]; } public void startWorkers() { for (int i 0; i threads.length; i) { threads[i] new WorkerThread(Worker- i, this); threads[i].start(); } } public void putRequest(Request request) { try { queue.put(request); } catch (InterruptedException e) { e.printStackTrace(); } } public Request takeRequest() { try { return queue.take(); } catch (InterruptedException e) { return null; } } } }18. 并发调试工具推荐VisualVM监控线程状态、内存使用JConsoleJMX监控工具JProfiler商业性能分析工具YourKit商业Java ProfilerArthas阿里开源的Java诊断工具jstack生成线程转储jcmd多功能命令行工具使用jstack分析死锁示例# 查找Java进程ID jps # 生成线程转储 jstack pid thread_dump.txt # 分析死锁 grep -A 10 deadlock thread_dump.txt19. 跨语言并发模型比较语言主要并发模型特点Java线程锁成熟、复杂、JVM优化GoGoroutineCSP轻量级、内置调度器ErlangActor模型容错性强、热代码升级Rust所有权无数据竞争编译时保证线程安全C线程原子操作各种库灵活、性能高、复杂度高JavaScript事件循环Promise/async-await单线程异步、WebWorker多线程20. 持续学习资源推荐书籍《Java并发编程实战》《并发编程的艺术》《Java并发编程之美》在线课程Coursera: Parallel Programming in JavaUdemy: Java Multithreading, Concurrency Performance Optimization开源项目NettyRxJavaAkka博客与社区InfoQ并发专栏美团技术团队博客Stack Overflow并发标签实践平台LeetCode多线程题目Codewars并发kata自己实现小型并发框架