本文基于JDK 17,结合真实项目经验,深入浅出讲解Java多线程技术

目录


一、为什么需要多线程?

1.1 真实场景引入

在我之前负责的电商系统中,遇到过这样一个问题:

public OrderResult createOrder(OrderRequest request) {
    // 1. 扣减库存 - 耗时 200ms
    inventoryService.deduct(request.getProductId(), request.getQuantity());
    
    // 2. 创建订单 - 耗时 150ms
    Order order = orderService.create(request);
    
    // 3. 发送短信通知 - 耗时 300ms
    smsService.sendOrderNotification(order);
    
    // 4. 记录日志 - 耗时 50ms
    logService.recordOrder(order);
    
    return new OrderResult(order);
}

问题:整个流程串行执行,总耗时 700ms,用户体验差。

优化后:使用多线程并行处理非核心流程

public OrderResult createOrder(OrderRequest request) {
    // 核心流程:扣减库存 + 创建订单(同步执行)
    inventoryService.deduct(request.getProductId(), request.getQuantity());
    Order order = orderService.create(request);
    
    // 非核心流程:异步执行
    CompletableFuture.runAsync(() -> 
        smsService.sendOrderNotification(order), asyncExecutor);
    CompletableFuture.runAsync(() -> 
        logService.recordOrder(order), asyncExecutor);
    
    return new OrderResult(order);
}

效果:响应时间从 700ms 降低到 350ms,提升 50%!

1.2 多线程的核心价值

  1. 提升响应速度:并行处理多个任务
  2. 提高资源利用率:充分利用多核CPU
  3. 改善用户体验:避免界面卡顿
  4. 增强系统吞吐量:同时处理更多请求

二、线程基础:从零开始

2.1 创建线程的四种方式

方式一:继承Thread类(不推荐)
public class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("线程执行:" + Thread.currentThread().getName());
    }
    
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start();
    }
}

缺点:Java单继承限制,无法继承其他类。

方式二:实现Runnable接口(推荐)
public class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("线程执行:" + Thread.currentThread().getName());
    }
    
    public static void main(String[] args) {
        Thread thread = new Thread(new MyRunnable());
        thread.start();
    }
}

优点:解耦任务和线程,可以继承其他类。

方式三:实现Callable接口(有返回值)
public class MyCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        Thread.sleep(1000);
        return "任务执行完成:" + Thread.currentThread().getName();
    }
    
    public static void main(String[] args) throws Exception {
        FutureTask<String> futureTask = new FutureTask<>(new MyCallable());
        new Thread(futureTask).start();
        
        // 获取返回值(会阻塞)
        String result = futureTask.get();
        System.out.println(result);
    }
}

优点:可以获取返回值,可以抛出异常。

方式四:线程池(生产环境首选)
public class ThreadPoolExample {
    private static final ExecutorService executor = 
        Executors.newFixedThreadPool(10);
    
    public static void main(String[] args) {
        executor.submit(() -> {
            System.out.println("线程池执行任务");
        });
        
        executor.shutdown();
    }
}

优点:复用线程,避免频繁创建销毁,性能最优。

2.2 线程生命周期

NEW(新建)
  ↓ start()
RUNNABLE(就绪/运行)
  ↓ 获取锁失败 / wait() / sleep() / join() / LockSupport.park()
BLOCKED / WAITING / TIMED_WAITING(阻塞/等待)
  ↓ 获取锁 / notify() / 超时 / interrupt()
RUNNABLE(就绪/运行)
  ↓ run()执行完毕
TERMINATED(终止)

2.3 实战案例:批量数据处理

场景:需要处理100万条数据,单线程太慢。

public class BatchProcessor {
    private static final int THREAD_COUNT = 8;
    private static final int BATCH_SIZE = 10000;
    
    public void processBigData(List<Data> dataList) throws InterruptedException {
        int totalSize = dataList.size();
        CountDownLatch latch = new CountDownLatch(THREAD_COUNT);
        
        for (int i = 0; i < THREAD_COUNT; i++) {
            int start = i * (totalSize / THREAD_COUNT);
            int end = (i == THREAD_COUNT - 1) ? totalSize : (i + 1) * (totalSize / THREAD_COUNT);
            
            new Thread(() -> {
                try {
                    List<Data> subList = dataList.subList(start, end);
                    for (Data data : subList) {
                        processData(data);
                    }
                } finally {
                    latch.countDown();
                }
            }, "Worker-" + i).start();
        }
        
        latch.await();
        System.out.println("所有数据处理完成");
    }
    
    private void processData(Data data) {
        // 具体业务逻辑
    }
}

性能对比

  • 单线程:100万条数据耗时 50秒
  • 8线程并行:100万条数据耗时 7秒
  • 性能提升 7倍!

三、线程安全问题与解决方案

3.1 经典问题:账户转账

不安全的代码

public class BankAccount {
    private int balance = 1000;
    
    public void transfer(BankAccount target, int amount) {
        if (this.balance >= amount) {
            this.balance -= amount;
            target.balance += amount;
        }
    }
}

问题:多线程并发转账时,可能出现余额不一致。

3.2 解决方案一:synchronized关键字

public class BankAccount {
    private int balance = 1000;
    
    public synchronized void transfer(BankAccount target, int amount) {
        if (this.balance >= amount) {
            this.balance -= amount;
            target.balance += amount;
        }
    }
}

注意:synchronized锁的是对象,要避免死锁!

死锁示例

// 线程1:A转账给B
accountA.transfer(accountB, 100);

// 线程2:B转账给A
accountB.transfer(accountA, 100);

解决死锁:按固定顺序加锁

public void transfer(BankAccount target, int amount) {
    BankAccount first = this.hashCode() < target.hashCode() ? this : target;
    BankAccount second = this.hashCode() < target.hashCode() ? target : this;
    
    synchronized (first) {
        synchronized (second) {
            if (this.balance >= amount) {
                this.balance -= amount;
                target.balance += amount;
            }
        }
    }
}

3.3 解决方案二:ReentrantLock

public class BankAccount {
    private int balance = 1000;
    private final ReentrantLock lock = new ReentrantLock();
    
    public void transfer(BankAccount target, int amount) {
        lock.lock();
        try {
            if (this.balance >= amount) {
                this.balance -= amount;
                target.balance += amount;
            }
        } finally {
            lock.unlock();
        }
    }
}

ReentrantLock vs synchronized

特性 synchronized ReentrantLock
使用方式 关键字,自动释放 API调用,手动释放
可中断 不支持 支持 tryLock()
公平锁 非公平 可选公平/非公平
条件变量 单个wait/notify 多个Condition
性能 JDK6后优化,相当 略优

实战建议

  • 简单场景用 synchronized
  • 需要高级特性用 ReentrantLock

3.4 解决方案三:原子类

public class Counter {
    private AtomicInteger count = new AtomicInteger(0);
    
    public void increment() {
        count.incrementAndGet();
    }
    
    public int getCount() {
        return count.get();
    }
}

常用原子类

  • AtomicIntegerAtomicLong:基本类型
  • AtomicReference:对象引用
  • AtomicStampedReference:解决ABA问题
  • LongAdder:高并发计数器(性能更优)

3.5 实战案例:高并发计数器

场景:统计网站访问量,每秒10万次请求。

public class VisitCounter {
    // 错误方式:synchronized性能差
    private int count = 0;
    public synchronized void increment() {
        count++;
    }
    
    // 正确方式:LongAdder性能优
    private LongAdder counter = new LongAdder();
    public void increment() {
        counter.increment();
    }
    
    public long getCount() {
        return counter.sum();
    }
}

性能测试

  • synchronized:100万次递增耗时 800ms
  • AtomicLong:100万次递增耗时 300ms
  • LongAdder:100万次递增耗时 50ms

LongAdder为什么快?

  • 内部使用分段锁思想
  • 多个线程操作不同的Cell
  • 最后汇总所有Cell的值

四、线程池:生产环境的最佳实践

4.1 为什么必须使用线程池?

反面教材:每次请求创建新线程

@RestController
public class UserController {
    @GetMapping("/user/{id}")
    public User getUser(@PathVariable Long id) {
        new Thread(() -> {
            logService.recordAccess(id);
        }).start();
        
        return userService.getById(id);
    }
}

问题

  1. 创建线程开销大(1MB栈空间)
  2. 无法控制并发数,可能OOM
  3. 线程频繁创建销毁,CPU浪费

4.2 线程池核心参数详解

public ThreadPoolExecutor(
    int corePoolSize,           // 核心线程数
    int maximumPoolSize,        // 最大线程数
    long keepAliveTime,         // 空闲线程存活时间
    TimeUnit unit,              // 时间单位
    BlockingQueue<Runnable> workQueue,  // 任务队列
    ThreadFactory threadFactory,        // 线程工厂
    RejectedExecutionHandler handler    // 拒绝策略
)

执行流程

提交任务
  ↓
核心线程数未满?
  ↓ 是
创建核心线程执行
  ↓ 否
任务队列未满?
  ↓ 是
加入队列等待
  ↓ 否
最大线程数未满?
  ↓ 是
创建非核心线程执行
  ↓ 否
执行拒绝策略

4.3 实战配置:不同场景的线程池

场景一:CPU密集型任务(计算为主)
@Configuration
public class ThreadPoolConfig {
    
    @Bean("cpuIntensiveExecutor")
    public ThreadPoolExecutor cpuIntensiveExecutor() {
        int coreCount = Runtime.getRuntime().availableProcessors();
        return new ThreadPoolExecutor(
            coreCount,              // 核心线程数 = CPU核心数
            coreCount,              // 最大线程数 = CPU核心数
            60L,
            TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(1000),
            new ThreadFactoryBuilder()
                .setNameFormat("cpu-pool-%d")
                .build(),
            new ThreadPoolExecutor.CallerRunsPolicy()
        );
    }
}

适用场景:数据加密、图像处理、复杂计算

场景二:IO密集型任务(网络/磁盘IO)
@Bean("ioIntensiveExecutor")
public ThreadPoolExecutor ioIntensiveExecutor() {
    int coreCount = Runtime.getRuntime().availableProcessors();
    return new ThreadPoolExecutor(
        coreCount * 2,          // 核心线程数 = CPU核心数 * 2
        coreCount * 4,          // 最大线程数 = CPU核心数 * 4
        60L,
        TimeUnit.SECONDS,
        new LinkedBlockingQueue<>(2000),
        new ThreadFactoryBuilder()
            .setNameFormat("io-pool-%d")
            .setUncaughtExceptionHandler((t, e) -> 
                log.error("线程异常:{}", t.getName(), e))
            .build(),
        new ThreadPoolExecutor.CallerRunsPolicy()
    );
}

适用场景:HTTP请求、数据库查询、文件读写

场景三:定时任务
@Bean("scheduledExecutor")
public ScheduledThreadPoolExecutor scheduledExecutor() {
    return new ScheduledThreadPoolExecutor(
        5,
        new ThreadFactoryBuilder()
            .setNameFormat("scheduled-pool-%d")
            .build()
    );
}

使用示例

@Service
public class ScheduledService {
    @Autowired
    private ScheduledThreadPoolExecutor scheduledExecutor;
    
    public void startScheduledTask() {
        scheduledExecutor.scheduleAtFixedRate(
            () -> cleanExpiredData(),
            0,              // 初始延迟
            1,              // 执行间隔
            TimeUnit.HOURS  // 时间单位
        );
    }
}

4.4 拒绝策略选择

策略 说明 适用场景
AbortPolicy(默认) 抛出异常 需要感知任务被拒绝
CallerRunsPolicy 调用者线程执行 重要任务不能丢失
DiscardPolicy 静默丢弃 允许丢失的任务
DiscardOldestPolicy 丢弃最旧任务 优先执行新任务

自定义拒绝策略

public class CustomRejectedHandler implements RejectedExecutionHandler {
    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        log.warn("任务被拒绝,当前队列大小:{}", executor.getQueue().size());
        
        try {
            executor.getQueue().offer(r, 3, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            log.error("任务重新提交失败", e);
        }
    }
}

4.5 实战案例:异步日志系统

@Component
public class AsyncLogService {
    private final ThreadPoolExecutor logExecutor;
    
    public AsyncLogService() {
        this.logExecutor = new ThreadPoolExecutor(
            2,
            4,
            60L,
            TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(10000),
            new ThreadFactoryBuilder()
                .setNameFormat("async-log-%d")
                .setDaemon(true)
                .build(),
            new ThreadPoolExecutor.DiscardOldestPolicy()
        );
    }
    
    public void logAsync(String message) {
        logExecutor.execute(() -> {
            try {
                writeToFile(message);
            } catch (Exception e) {
                System.err.println("日志写入失败:" + e.getMessage());
            }
        });
    }
    
    private void writeToFile(String message) {
        // 写入文件逻辑
    }
    
    @PreDestroy
    public void shutdown() {
        logExecutor.shutdown();
        try {
            if (!logExecutor.awaitTermination(10, TimeUnit.SECONDS)) {
                logExecutor.shutdownNow();
            }
        } catch (InterruptedException e) {
            logExecutor.shutdownNow();
        }
    }
}

4.6 线程池监控

@Component
@Slf4j
public class ThreadPoolMonitor {
    
    @Scheduled(fixedRate = 60000)
    public void monitorThreadPool() {
        ThreadPoolExecutor executor = getExecutor();
        
        log.info("线程池监控 - " +
            "核心线程数:{}, " +
            "活跃线程数:{}, " +
            "最大线程数:{}, " +
            "队列大小:{}, " +
            "已完成任务数:{}, " +
            "总任务数:{}",
            executor.getCorePoolSize(),
            executor.getActiveCount(),
            executor.getMaximumPoolSize(),
            executor.getQueue().size(),
            executor.getCompletedTaskCount(),
            executor.getTaskCount()
        );
        
        if (executor.getQueue().size() > 1000) {
            log.warn("线程池队列积压严重,考虑扩容!");
        }
    }
}

五、并发工具类实战

5.1 CountDownLatch:等待多个线程完成

场景:系统启动时需要预加载多个资源

@Component
public class SystemInitializer {
    
    public void initialize() throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(3);
        
        new Thread(() -> {
            try {
                loadConfig();
            } finally {
                latch.countDown();
            }
        }).start();
        
        new Thread(() -> {
            try {
                loadCache();
            } finally {
                latch.countDown();
            }
        }).start();
        
        new Thread(() -> {
            try {
                connectDatabase();
            } finally {
                latch.countDown();
            }
        }).start();
        
        latch.await(30, TimeUnit.SECONDS);
        System.out.println("系统初始化完成");
    }
}

5.2 CyclicBarrier:循环栅栏

场景:多线程分段计算,每个阶段需要同步

public class ParallelCalculator {
    private final CyclicBarrier barrier;
    private final int threadCount = 4;
    private final double[] results = new double[threadCount];
    
    public ParallelCalculator() {
        this.barrier = new CyclicBarrier(threadCount, () -> {
            double sum = Arrays.stream(results).sum();
            System.out.println("本轮计算完成,总和:" + sum);
        });
    }
    
    public void calculate() {
        for (int i = 0; i < threadCount; i++) {
            final int index = i;
            new Thread(() -> {
                for (int round = 0; round < 3; round++) {
                    results[index] = Math.random() * 100;
                    try {
                        barrier.await();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }
}

5.3 Semaphore:信号量限流

场景:限制同时访问数据库的连接数

@Component
public class DatabaseConnectionPool {
    private final Semaphore semaphore = new Semaphore(10);
    
    public void executeQuery(String sql) {
        try {
            semaphore.acquire();
            try {
                doQuery(sql);
            } finally {
                semaphore.release();
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
    
    private void doQuery(String sql) {
        // 执行数据库查询
    }
}

5.4 CompletableFuture:异步编排神器

场景:商品详情页需要并行查询多个服务

@Service
public class ProductDetailService {
    
    public ProductDetailVO getProductDetail(Long productId) {
        CompletableFuture<Product> productFuture = 
            CompletableFuture.supplyAsync(() -> 
                productService.getById(productId));
        
        CompletableFuture<List<Comment>> commentFuture = 
            CompletableFuture.supplyAsync(() -> 
                commentService.listByProductId(productId));
        
        CompletableFuture<Inventory> inventoryFuture = 
            CompletableFuture.supplyAsync(() -> 
                inventoryService.getByProductId(productId));
        
        CompletableFuture<Void> allFuture = CompletableFuture.allOf(
            productFuture, commentFuture, inventoryFuture
        );
        
        allFuture.join();
        
        return ProductDetailVO.builder()
            .product(productFuture.join())
            .comments(commentFuture.join())
            .inventory(inventoryFuture.join())
            .build();
    }
}

高级用法:异常处理和超时控制

public CompletableFuture<String> callExternalApi() {
    return CompletableFuture.supplyAsync(() -> {
        return httpClient.get("https://api.example.com/data");
    })
    .orTimeout(3, TimeUnit.SECONDS)
    .exceptionally(ex -> {
        log.error("API调用失败", ex);
        return "默认值";
    });
}

六、常见并发问题排查

6.1 死锁排查

死锁代码示例

public class DeadLockDemo {
    private static final Object lock1 = new Object();
    private static final Object lock2 = new Object();
    
    public static void main(String[] args) {
        new Thread(() -> {
            synchronized (lock1) {
                System.out.println("线程1获取lock1");
                try { Thread.sleep(100); } catch (InterruptedException e) {}
                synchronized (lock2) {
                    System.out.println("线程1获取lock2");
                }
            }
        }).start();
        
        new Thread(() -> {
            synchronized (lock2) {
                System.out.println("线程2获取lock2");
                try { Thread.sleep(100); } catch (InterruptedException e) {}
                synchronized (lock1) {
                    System.out.println("线程2获取lock1");
                }
            }
        }).start();
    }
}

排查方法

  1. 使用jstack命令
# 获取进程ID
jps

# 导出线程堆栈
jstack <pid> > thread_dump.txt

# 查找死锁信息
grep -A 20 "Found one Java-level deadlock" thread_dump.txt
  1. 使用JConsole可视化工具

    • 连接到Java进程
    • 查看"线程"标签
    • 点击"检测死锁"按钮
  2. 代码预防

public boolean tryTransfer(BankAccount target, int amount, long timeout) {
    long startTime = System.currentTimeMillis();
    
    while (System.currentTimeMillis() - startTime < timeout) {
        if (this.lock.tryLock()) {
            try {
                if (target.lock.tryLock()) {
                    try {
                        if (this.balance >= amount) {
                            this.balance -= amount;
                            target.balance += amount;
                            return true;
                        }
                    } finally {
                        target.lock.unlock();
                    }
                }
            } finally {
                this.lock.unlock();
            }
        }
        try { Thread.sleep(10); } catch (InterruptedException e) {}
    }
    return false;
}

6.2 内存泄漏排查

常见原因:ThreadLocal未清理

public class ThreadLocalLeakDemo {
    private static ThreadLocal<byte[]> threadLocal = new ThreadLocal<>();
    
    public void process() {
        threadLocal.set(new byte[1024 * 1024]);
        
        // 忘记清理,导致内存泄漏
    }
}

正确做法

public void process() {
    try {
        threadLocal.set(new byte[1024 * 1024]);
        // 业务逻辑
    } finally {
        threadLocal.remove();
    }
}

排查工具

# 导出堆内存快照
jmap -dump:live,format=b,file=heap.hprof <pid>

# 使用MAT工具分析heap.hprof文件
# 查找Dominator Tree,找出占用内存最大的对象

6.3 CPU飙高排查

步骤

  1. 找出占用CPU最高的线程
# 查看进程CPU使用率
top -p <pid>

# 查看线程CPU使用率
top -Hp <pid>

# 记录占用CPU最高的线程ID(十进制)
# 假设线程ID为 12345
  1. 转换线程ID为十六进制
printf "%x\n" 12345
# 输出:3039
  1. 导出线程堆栈并查找
jstack <pid> | grep -A 50 "0x3039"
  1. 分析代码
// 常见原因:死循环
while (true) {
    // 没有sleep或wait,CPU空转
}

// 正确做法:
while (running) {
    try {
        doWork();
        Thread.sleep(100);
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        break;
    }
}

6.4 线程池队列积压

监控告警

@Component
@Slf4j
public class ThreadPoolAlertMonitor {
    
    @Scheduled(fixedRate = 10000)
    public void checkThreadPool() {
        ThreadPoolExecutor executor = getExecutor();
        
        int queueSize = executor.getQueue().size();
        int activeCount = executor.getActiveCount();
        int poolSize = executor.getPoolSize();
        
        if (queueSize > 1000) {
            log.error("线程池队列积压严重!队列大小:{}, 活跃线程:{}, 线程池大小:{}", 
                queueSize, activeCount, poolSize);
            
            sendAlert("线程池队列积压告警");
        }
        
        if (activeCount == poolSize && queueSize > 0) {
            log.warn("线程池已满负荷运行,考虑扩容");
        }
    }
}

七、性能优化技巧

7.1 减少锁的粒度

优化前

public class UserService {
    private final Map<Long, User> userCache = new HashMap<>();
    
    public synchronized User getUser(Long id) {
        return userCache.get(id);
    }
    
    public synchronized void putUser(User user) {
        userCache.put(user.getId(), user);
    }
}

优化后:使用ConcurrentHashMap

public class UserService {
    private final ConcurrentHashMap<Long, User> userCache = new ConcurrentHashMap<>();
    
    public User getUser(Long id) {
        return userCache.get(id);
    }
    
    public void putUser(User user) {
        userCache.put(user.getId(), user);
    }
}

性能提升:并发读写性能提升10倍以上!

7.2 使用局部变量避免共享

优化前

public class Calculator {
    private int result = 0;
    
    public synchronized void calculate(int value) {
        result += value;
        result *= 2;
        result -= 10;
    }
}

优化后

public class Calculator {
    private AtomicInteger result = new AtomicInteger(0);
    
    public void calculate(int value) {
        int temp = value;
        temp *= 2;
        temp -= 10;
        result.addAndGet(temp);
    }
}

7.3 批量操作减少锁竞争

优化前

for (int i = 0; i < 10000; i++) {
    cache.put(i, "value" + i);
}

优化后

Map<Integer, String> batch = new HashMap<>();
for (int i = 0; i < 10000; i++) {
    batch.put(i, "value" + i);
}
cache.putAll(batch);

7.4 使用无锁数据结构

性能对比

数据结构 并发性能 适用场景
Vector 不推荐
Collections.synchronizedList 不推荐
CopyOnWriteArrayList 读多写少 配置列表
ConcurrentLinkedQueue 优秀 任务队列
LinkedBlockingQueue 良好 生产者消费者

八、面试高频问题

8.1 synchronized和ReentrantLock的区别?

答案要点

  1. 实现层面

    • synchronized是JVM层面的关键字
    • ReentrantLock是JDK层面的API
  2. 功能对比

    • synchronized自动释放锁,ReentrantLock需要手动释放
    • ReentrantLock支持tryLock()尝试获取锁
    • ReentrantLock支持公平锁和非公平锁
    • ReentrantLock支持多个Condition条件变量
  3. 性能:JDK6后synchronized优化,性能相当

  4. 使用建议:简单场景用synchronized,需要高级特性用ReentrantLock

8.2 volatile关键字的作用?

答案要点

  1. 保证可见性:一个线程修改后,其他线程立即可见
  2. 禁止指令重排序:保证有序性
  3. 不保证原子性:i++操作不是原子的

代码示例

public class VolatileDemo {
    private volatile boolean flag = false;
    
    public void writer() {
        flag = true;
    }
    
    public void reader() {
        if (flag) {
            // 一定能看到flag的最新值
        }
    }
}

8.3 线程池参数如何设置?

答案要点

  1. CPU密集型:核心线程数 = CPU核心数 + 1
  2. IO密集型:核心线程数 = CPU核心数 * 2
  3. 混合型:根据实际压测调整

计算公式

线程数 = CPU核心数 * (1 + 等待时间/计算时间)

示例

  • CPU核心数:8
  • 等待时间:90ms(IO操作)
  • 计算时间:10ms
  • 线程数 = 8 * (1 + 90/10) = 80

8.4 如何避免死锁?

答案要点

  1. 按顺序加锁:所有线程按相同顺序获取锁
  2. 使用tryLock:设置超时时间,获取失败则放弃
  3. 减少锁的持有时间:尽快释放锁
  4. 使用并发工具类:如ConcurrentHashMap

8.5 ThreadLocal原理和应用场景?

原理

// 每个Thread对象都有一个ThreadLocalMap
class Thread {
    ThreadLocal.ThreadLocalMap threadLocals;
}

// ThreadLocal的get方法
public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = t.threadLocals;
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            return (T)e.value;
        }
    }
    return setInitialValue();
}

应用场景

  1. 数据库连接管理
public class ConnectionManager {
    private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<>();
    
    public static Connection getConnection() {
        Connection conn = connectionHolder.get();
        if (conn == null) {
            conn = DriverManager.getConnection(url);
            connectionHolder.set(conn);
        }
        return conn;
    }
    
    public static void closeConnection() {
        Connection conn = connectionHolder.get();
        if (conn != null) {
            conn.close();
            connectionHolder.remove();
        }
    }
}
  1. 用户上下文传递
public class UserContext {
    private static ThreadLocal<User> userHolder = new ThreadLocal<>();
    
    public static void setUser(User user) {
        userHolder.set(user);
    }
    
    public static User getUser() {
        return userHolder.get();
    }
    
    public static void clear() {
        userHolder.remove();
    }
}

注意事项:使用完必须remove(),否则内存泄漏!


总结

核心要点回顾

  1. 线程基础

    • 优先使用线程池,避免直接new Thread
    • 理解线程生命周期和状态转换
  2. 线程安全

    • 简单场景用synchronized
    • 高性能场景用原子类
    • 复杂场景用ReentrantLock
  3. 线程池

    • CPU密集型:线程数 = CPU核心数
    • IO密集型:线程数 = CPU核心数 * 2
    • 必须监控线程池状态
  4. 并发工具

    • CountDownLatch:等待多个线程完成
    • CyclicBarrier:循环栅栏
    • Semaphore:限流
    • CompletableFuture:异步编排
  5. 性能优化

    • 减少锁的粒度
    • 使用无锁数据结构
    • 批量操作减少竞争

实战建议

  1. 开发阶段

    • 代码review关注线程安全
    • 使用FindBugs等工具检测并发问题
    • 编写并发测试用例
  2. 测试阶段

    • 压力测试验证并发性能
    • 使用JMeter模拟高并发场景
    • 监控线程池和内存使用
  3. 生产环境

    • 配置线程池监控告警
    • 定期分析线程dump
    • 关注CPU和内存指标

学习资源推荐

  1. 书籍

    • 《Java并发编程实战》
    • 《Java并发编程的艺术》
    • 《深入理解Java虚拟机》
  2. 实践项目

    • 实现一个线程安全的LRU缓存
    • 开发一个高性能的任务调度系统
    • 构建一个并发限流组件
  3. 持续学习

    • 关注JDK新版本的并发特性
    • 学习虚拟线程(JDK 21+)
    • 研究响应式编程(Reactor、RxJava)

附录:常用命令速查

JVM诊断命令

# 查看Java进程
jps -l

# 查看JVM参数
jinfo -flags <pid>

# 查看堆内存使用
jmap -heap <pid>

# 导出堆内存快照
jmap -dump:live,format=b,file=heap.hprof <pid>

# 查看线程堆栈
jstack <pid> > thread.txt

# 查看GC情况
jstat -gc <pid> 1000 10

# 实时监控
jconsole
jvisualvm

线程状态分析

# 统计线程状态
jstack <pid> | grep "java.lang.Thread.State" | sort | uniq -c

# 查找BLOCKED线程
jstack <pid> | grep -A 10 "BLOCKED"

# 查找WAITING线程
jstack <pid> | grep -A 10 "WAITING"

作者寄语:并发编程是Java开发的核心技能,需要理论结合实践。建议从简单场景入手,逐步深入,多写代码多调试。遇到问题不要慌,善用工具分析,一定能成为并发编程高手!

如果本文对您有帮助,欢迎点赞收藏!有问题欢迎评论区交流讨论。


关键词:Java多线程、并发编程、线程池、synchronized、ReentrantLock、ThreadLocal、CompletableFuture、死锁、性能优化

原创声明:本文为作者原创,基于多年实战经验总结,转载请注明出处。

更多推荐