Java 并发编程深度解析:从线程基础到高并发实战

一、并发编程核心概念

1.1 进程与线程

  • 进程:操作系统资源分配的基本单位,每个进程拥有独立的内存空间和系统资源。
  • 线程:CPU 调度的最小单位,共享所属进程的资源,线程间切换成本低于进程。
  • 协程(Loom 项目):JDK 19 + 引入的轻量级线程,基于用户态调度,可大幅降低高并发场景下的线程开销(目前为预览特性)。

1.2 Java 线程生命周期

Java 线程状态包括以下六种:

状态 描述 触发条件
NEW 新建状态,尚未启动 new Thread()
RUNNABLE 可运行状态(包含就绪和运行) 调用start()
BLOCKED 阻塞于锁,等待synchronized 竞争锁失败
WAITING 无限期等待,需手动唤醒 调用wait()/join()
TIMED_WAITING 有限期等待,自动唤醒 调用sleep(ms)/wait(ms)
TERMINATED 终止状态,线程执行完毕 run()方法执行结束

二、线程创建与管理

2.1 三种创建方式

方式 1:继承Thread
class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("Thread running");
    }
}
// 启动线程
new MyThread().start();
方式 2:实现Runnable接口
Runnable task = () -> System.out.println("Runnable task");
new Thread(task).start();
方式 3:FutureTask+Callable(支持返回值和异常处理)
import java.util.concurrent.*;

Callable callable = () -> {
    TimeUnit.SECONDS.sleep(1); // 模拟耗时操作
    return 42; // 可返回结果
};
FutureTask futureTask = new FutureTask<>(callable);
new Thread(futureTask).start();

try {
    Integer result = futureTask.get(); // 获取返回值
    System.out.println("Result: " + result);
} catch (InterruptedException | ExecutionException e) {
    e.printStackTrace();
}

2.2 线程池核心参数(ThreadPoolExecutor

ThreadPoolExecutor executor = new ThreadPoolExecutor(
    5,            // 核心线程数:线程池长期维持的最小线程数
    10,           // 最大线程数:允许创建的最大线程数
    60L,          // 空闲线程存活时间:超过核心线程数的线程空闲后存活时间
    TimeUnit.SECONDS, // 时间单位
    new LinkedBlockingQueue<>(100), // 工作队列:存储待执行任务
    Executors.defaultThreadFactory(), // 线程工厂:创建线程的工厂
    new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略:任务无法处理时的策略
);

常见拒绝策略

  • AbortPolicy(默认):抛出RejectedExecutionException
  • CallerRunsPolicy:由调用者线程处理任务
  • DiscardPolicy:丢弃任务
  • DiscardOldestPolicy:丢弃队列中最旧的任务

三、线程同步机制

3.1 synchronized关键字(隐式锁)

实例方法锁:锁对象为当前实例
public synchronized void instanceMethod() {
    // 临界区代码(锁为this)
}
静态方法锁:锁对象为类的Class对象
public static synchronized void staticMethod() {
    // 临界区代码(锁为Class对象)
}
代码块锁:自定义锁对象
private final Object lock = new Object();

public void blockLock() {
    synchronized (lock) { // 显式指定锁对象
        // 临界区代码
    }
}

3.2 ReentrantLock显式锁(可中断、支持公平锁)

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

Lock lock = new ReentrantLock(); // 默认为非公平锁
// 可通过参数true启用公平锁:new ReentrantLock(true)

lock.lock(); // 阻塞获取锁
try {
    // 临界区代码
} finally {
    lock.unlock(); // 必须在finally中释放锁
}

// 可中断获取锁(支持线程中断)
try {
    if (lock.tryLock(1, TimeUnit.SECONDS)) { // 带超时的尝试获取
        try { /* ... */ }
    } else {
        // 超时处理
    }
} catch (InterruptedException e) {
    Thread.currentThread().interrupt(); // 恢复中断状态
}

3.3 synchronized vs ReentrantLock对比

特性 synchronized ReentrantLock
实现机制 JVM 内置(基于 Monitor 锁) JDK 代码实现(基于 AQS)
锁释放 自动释放(出作用域) 必须手动调用unlock()
公平锁 不支持 支持(构造参数fair
条件变量 单一wait/notify 支持多个Condition对象
可中断性 不支持 支持lockInterruptibly()
适用场景 简单同步场景 复杂逻辑(如超时、公平锁)

四、并发工具类

4.1 CountDownLatch(倒计时门闩)

场景:主线程等待多个子线程完成任务。

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        int threadCount = 3;
        CountDownLatch latch = new CountDownLatch(threadCount);
        ExecutorService executor = Executors.newFixedThreadPool(threadCount);

        for (int i = 0; i < threadCount; i++) {
            executor.execute(() -> {
                try {
                    // 模拟任务执行
                    Thread.sleep(1000);
                    System.out.println(Thread.currentThread().getName() + " 任务完成");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    latch.countDown(); // 计数器减1
                }
            });
        }

        latch.await(); // 阻塞当前线程,直到计数器为0
        System.out.println("所有任务完成");
        executor.shutdown();
    }
}

4.2 CyclicBarrier(循环屏障)

场景:多个线程相互等待,全部到达屏障后再继续执行。

import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CyclicBarrierDemo {
    private static final int PARTIES = 3;

    public static void main(String[] args) {
        CyclicBarrier barrier = new CyclicBarrier(PARTIES, () -> 
            System.out.println("所有线程到达屏障,继续执行")); // 可选屏障突破回调

        ExecutorService executor = Executors.newFixedThreadPool(PARTIES);
        for (int i = 0; i < PARTIES; i++) {
            executor.execute(() -> {
                try {
                    System.out.println(Thread.currentThread().getName() + " 等待屏障");
                    barrier.await(); // 等待其他线程
                    System.out.println(Thread.currentThread().getName() + " 继续执行");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
        }
        executor.shutdown();
    }
}

4.3 Semaphore(信号量)

场景:控制同时访问共享资源的线程数(如限流)。

import java.util.concurrent.Semaphore;

public class SemaphoreDemo {
    private static final int PERMITS = 3; // 允许同时访问的线程数
    private static final Semaphore semaphore = new Semaphore(PERMITS);

    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                try {
                    semaphore.acquire(); // 获取许可(阻塞直到有可用许可)
                    System.out.println(Thread.currentThread().getName() + " 开始访问资源");
                    Thread.sleep(1000); // 模拟资源访问耗时
                    System.out.println(Thread.currentThread().getName() + " 释放资源");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    semaphore.release(); // 释放许可
                }
            }).start();
        }
    }
}

五、并发集合类

5.1 ConcurrentHashMap(线程安全的哈希表)

import java.util.concurrent.ConcurrentHashMap;

public class ConcurrentHashMapDemo {
    public static void main(String[] args) {
        ConcurrentHashMap map = new ConcurrentHashMap<>();
        map.put("a", 1); // 线程安全的put操作
        map.putIfAbsent("b", 2); // 仅当键不存在时插入

        // 原子更新操作:计算值(若不存在则初始化)
        map.compute("c", (k, v) -> v != null ? v + 1 : 1);
        map.computeIfAbsent("d", k -> 0); // 等价于putIfAbsent

        // 线程安全的遍历
        map.forEach((k, v) -> System.out.println(k + ": " + v));
    }
}

5.2 CopyOnWriteArrayList(写时复制列表)

特点:迭代时不抛出ConcurrentModificationException,适用于读多写少场景。

import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;

public class CopyOnWriteArrayListDemo {
    public static void main(String[] args) {
        CopyOnWriteArrayList list = new CopyOnWriteArrayList<>();
        list.add("item1");
        list.add("item2");

        // 安全的迭代(基于快照机制)
        Iterator iterator = list.iterator();
        while (iterator.hasNext()) {
            String item = iterator.next();
            System.out.println(item);
            if ("item1".equals(item)) {
                list.add("item3"); // 写操作会创建新数组,不影响当前迭代
            }
        }
    }
}

5.3 BlockingQueue(阻塞队列)

场景:线程间安全通信(生产者 - 消费者模型)。

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

public class BlockingQueueDemo {
    private static final BlockingQueue queue = new LinkedBlockingQueue<>(10); // 有界队列

    // 生产者线程
    static class Producer implements Runnable {
        @Override
        public void run() {
            try {
                for (int i = 0; i < 5; i++) {
                    String data = "Data-" + i;
                    queue.put(data); // 队列满时阻塞
                    System.out.println("生产:" + data);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }

    // 消费者线程
    static class Consumer implements Runnable {
        @Override
        public void run() {
            try {
                while (!Thread.currentThread().isInterrupted()) {
                    String data = queue.take(); // 队列为空时阻塞
                    System.out.println("消费:" + data);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }

    public static void main(String[] args) {
        new Thread(new Producer()).start();
        new Thread(new Consumer()).start();
    }
}

六、原子操作类

6.1 AtomicInteger(原子整数)

import java.util.concurrent.atomic.AtomicInteger;

public class AtomicIntegerDemo {
    private static AtomicInteger counter = new AtomicInteger(0);

    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                counter.incrementAndGet(); // 原子自增(等价于++i)
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                counter.getAndIncrement(); // 原子自增(等价于i++)
            }
        });

        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();

        System.out.println("最终值:" + counter.get()); // 输出:2000
    }
}

6.2 LongAdder(高并发累加器)

原理:分段锁优化,将数值拆分到多个变量,减少 CAS 竞争。

import java.util.concurrent.atomic.LongAdder;

public class LongAdderDemo {
    private static LongAdder adder = new LongAdder();

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                for (int j = 0; j < 10000; j++) {
                    adder.add(1); // 累加操作
                }
            }).start();
        }

        Thread.sleep(1000); // 等待所有线程完成
        System.out.println("总和:" + adder.sum()); // 输出:100000
    }
}

6.3 CAS 原理(Compare-And-Swap)

核心思想:通过硬件指令实现无锁原子操作,仅当值等于预期值时更新。

// 模拟CAS操作(非线程安全,仅演示逻辑)
public class SimulatedCAS {
    private int value;

    // 模拟CAS更新(需配合volatile保证可见性)
    public int compareAndSwap(int expected, int newValue) {
        int oldValue = value; // 读取当前值
        if (oldValue == expected) { // 比较预期值
            value = newValue; // 执行更新
        }
        return oldValue; // 返回旧值
    }
}

七、内存模型(JMM)与volatile

7.1 Java 内存模型(JMM)

  • 主内存:所有线程共享的内存区域,存储对象实例和数组。
  • 工作内存:每个线程私有的内存区域,存储主内存变量的副本。
  • 关键特性
    • 线程间通信需通过主内存(工作内存→主内存→工作内存)。
    • 保证可见性、有序性、原子性(部分场景需配合同步机制)。

7.2 volatile关键字

作用

  1. 保证可见性:修改后强制刷新主内存,读取时强制从主内存获取最新值。
  2. 禁止指令重排序:通过内存屏障(Memory Barrier)保证操作顺序。

示例:线程安全的标志位

public class VolatileDemo {
    private volatile boolean running = true; // 保证可见性

    // 生产者线程:修改标志位
    public void stop() {
        running = false; // 写操作,强制刷新主内存
    }

    // 消费者线程:读取标志位
    public void process() {
        while (running) { // 读操作,强制从主内存获取
            // 执行任务
        }
    }
}

八、死锁分析与避免

8.1 死锁产生条件(四者必须同时满足)

  1. 互斥条件:资源被线程独占,其他线程无法获取。
  2. 请求与保持条件:线程持有资源的同时请求其他资源。
  3. 不可剥夺条件:资源只能由持有者主动释放,无法强制剥夺。
  4. 循环等待条件:线程间形成资源请求环路(A 等 B,B 等 A)。

8.2 诊断工具

命令行工具:
  • jps:查看 Java 进程 PID。
  • jstack :打印线程堆栈信息,定位死锁。
  • jcmd Thread.print:生成线程转储文件(JDK 7+)。
输出示例(死锁线程):
Found one Java-level deadlock:
=============================
"Thread-0":
  waiting to lock monitor 0x0000... (object x),
  which is held by "Thread-1"
"Thread-1":
  waiting to lock monitor 0

你可能感兴趣的:(java,jvm,开发语言)