Java并发编程深度指南:多线程

一、前言:为什么多线程如此重要?

在现代软件开发中,多线程编程已成为必备的核心技能。随着多核处理器的普及和分布式系统的发展,能否编写高效、安全的并发程序直接决定了应用程序的性能和稳定性。Java作为企业级开发的主流语言,提供了丰富而强大的并发API,掌握这些技术不仅有助于日常开发,更是面试中的高频考点。

本文将系统性地讲解Java多线程的核心概念、实战技巧和面试要点,帮助读者构建完整的知识体系。


二、Java多线程基础概念

2.1 进程与线程的区别

  • ​进程​​:操作系统资源分配的基本单位,拥有独立的内存空间

  • ​线程​​:CPU调度的基本单位,共享进程资源,切换开销小

2.2 线程的创建方式

// 1. 继承Thread类
class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("Thread running");
    }
}

// 2. 实现Runnable接口(推荐)
class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("Runnable running");
    }
}

// 3. 实现Callable接口(可获取返回值)
class MyCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        return "Callable result";
    }
}

// 4. 使用线程池(生产环境推荐)
ExecutorService executor = Executors.newFixedThreadPool(10);

2.3 线程生命周期

  • NEW:新建状态

  • RUNNABLE:可运行状态

  • BLOCKED:阻塞状态

  • WAITING:等待状态

  • TIMED_WAITING:超时等待状态

  • TERMINATED:终止状态


三、线程安全与同步机制

3.1 临界区与竞态条件

当多个线程同时访问共享资源时,如果没有正确的同步措施,会导致数据不一致问题。

3.2 synchronized关键字

// 同步方法
public synchronized void syncMethod() {
    // 临界区代码
}

// 同步代码块
public void method() {
    synchronized(this) {
        // 临界区代码
    }
}

// 静态方法同步
public static synchronized void staticSyncMethod() {
    // 临界区代码
}

3.3 volatile关键字

保证变量的可见性和禁止指令重排序,但不保证原子性。

private volatile boolean flag = false;

3.4 Java锁体系

3.4.1 ReentrantLock
private final ReentrantLock lock = new ReentrantLock();

public void method() {
    lock.lock();
    try {
        // 临界区代码
    } finally {
        lock.unlock();
    }
}
3.4.2 读写锁(ReadWriteLock)
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();

public void read() {
    rwLock.readLock().lock();
    try {
        // 读操作
    } finally {
        rwLock.readLock().unlock();
    }
}

public void write() {
    rwLock.writeLock().lock();
    try {
        // 写操作
    } finally {
        rwLock.writeLock().unlock();
    }
}

四、Java并发工具类

4.1 CountDownLatch

等待多个线程完成后再执行主线程

CountDownLatch latch = new CountDownLatch(3);

// 工作线程
latch.countDown();

// 主线程
latch.await();

4.2 CyclicBarrier

让一组线程到达屏障点时被阻塞,直到最后一个线程到达

CyclicBarrier barrier = new CyclicBarrier(3, () -> {
    System.out.println("所有线程已就位");
});

// 线程中调用
barrier.await();

4.3 Semaphore

控制同时访问特定资源的线程数量

Semaphore semaphore = new Semaphore(5); // 允许5个线程同时访问

semaphore.acquire();
try {
    // 使用资源
} finally {
    semaphore.release();
}

4.4 Exchanger

两个线程间交换数据

Exchanger<String> exchanger = new Exchanger<>();

// 线程1
String data1 = exchanger.exchange("Data from thread1");

// 线程2
String data2 = exchanger.exchange("Data from thread2");

五、线程池深入解析

5.1 为什么使用线程池?

  • 降低资源消耗:减少线程创建和销毁的开销

  • 提高响应速度:任务到达时无需等待线程创建

  • 提高线程可管理性:统一分配、监控和调优

5.2 ThreadPoolExecutor核心参数

ThreadPoolExecutor executor = new ThreadPoolExecutor(
    corePoolSize,     // 核心线程数
    maximumPoolSize,  // 最大线程数
    keepAliveTime,    // 空闲线程存活时间
    unit,             // 时间单位
    workQueue,        // 工作队列
    threadFactory,    // 线程工厂
    handler           // 拒绝策略
);

5.3 四种拒绝策略

  • AbortPolicy:抛出RejectedExecutionException(默认)

  • CallerRunsPolicy:由调用线程执行该任务

  • DiscardPolicy:直接丢弃任务

  • DiscardOldestPolicy:丢弃队列最前面的任务

5.4 Executors工具类提供的线程池

  • newFixedThreadPool:固定大小线程池

  • newCachedThreadPool:可缓存线程池

  • newSingleThreadExecutor:单线程线程池

  • newScheduledThreadPool:定时任务线程池


六、原子操作类

Java提供了java.util.concurrent.atomic包,解决原子性问题

// 原子整型
AtomicInteger atomicInt = new AtomicInteger(0);
atomicInt.incrementAndGet(); // 原子自增

// 原子引用
AtomicReference<String> atomicRef = new AtomicReference<>("初始值");

// CAS操作
boolean success = atomicInt.compareAndSet(expect, update);

七、面试常见问题与解答

7.1 基础概念类

​Q1: synchronized和ReentrantLock的区别?​

  • synchronized是JVM级别,ReentrantLock是API级别

  • ReentrantLock提供更灵活的锁机制:可中断、可轮询、定时锁等

  • synchronized会自动释放锁,ReentrantLock需要手动释放

​Q2: volatile和synchronized的区别?​

  • volatile保证可见性和有序性,不保证原子性

  • synchronized保证原子性、可见性和有序性

  • volatile不会造成线程阻塞,synchronized可能造成阻塞

7.2 实战应用类

​Q3: 如何避免死锁?​

  • 避免一个线程同时获取多个锁

  • 使用定时锁tryLock(timeout)

  • 保持锁的获取顺序一致

  • 使用较粗粒度的锁

​Q4: 线程池的工作流程?​

  1. 提交任务后,先判断核心线程是否已满

  2. 未满则创建新线程执行任务

  3. 已满则将任务放入工作队列

  4. 队列已满则判断线程数是否达到最大值

  5. 未达到则创建新线程,已达到则执行拒绝策略

7.3 高级特性类

​Q5: ThreadLocal原理及应用场景?​

  • 每个线程有独立的ThreadLocalMap存储数据

  • 应用场景:数据库连接、Session管理、参数传递等

  • 注意内存泄漏问题,使用后及时remove()

​Q6: AQS(AbstractQueuedSynchronizer)原理?​

  • 使用CLH队列管理获取锁的线程

  • 通过state变量表示同步状态

  • 提供了acquire()和release()等模板方法

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值