Java多线程

1. 基本概念

进程 vs 线程

  • 进程:资源分配的最小单位,包含代码、数据和系统资源(如内存、文件句柄)。不同进程内存隔离。
  • 线程:CPU调度的最小单位,隶属于进程,共享进程内存空间。一个进程可包含多个线程,实现并发执行。

2. 线程实现方式

继承 Thread

class MyThread extends Thread {
    public void run() { /* 任务逻辑 */ }
}
MyThread t = new MyThread();
t.start(); 

实现 Runnable 接口

class MyRunnable implements Runnable {
    public void run() { /* 任务逻辑 */ }
}
Thread t = new Thread(new MyRunnable());
t.start();

实现 Callable 接口(带返回值)

class MyCallable implements Callable<String> {
    public String call() { return "结果"; }
}
FutureTask<String> future = new FutureTask<>(new MyCallable());
new Thread(future).start();
String result = future.get();  

线程池(高效管理线程)

核心参数

  • 核心线程数:常驻线程数量。
  • 最大线程数:临时线程上限(任务队列满时启用)。
  • 任务队列:如 LinkedBlockingQueue(无界队列,易内存溢出)
ExecutorService pool = Executors.newFixedThreadPool(5);  // 固定大小线程池
pool.submit(() -> { /* 任务逻辑 */ }); 

3. 线程生命周期

  • 新建 (New):通过 new Thread() 创建,未调用 start()
  • 就绪 (Runnable):调用 start() 后,等待CPU调度。
  • 运行 (Running):获得CPU时间片,执行 run() 方法。
  • 阻塞 (Blocked)
    • 等待阻塞:如 wait()sleep()join()
    • 同步阻塞:竞争锁失败(如 synchronized 锁)。
  • 终止 (Terminated)run() 执行完毕或异常终止。

4. 同步与锁机制

synchronized 关键字

  • 修饰代码块

    synchronized (锁对象) { /* 临界区代码 */ }  // 锁对象需唯一 
    
  • 修饰方法:锁为当前对象实例(实例方法)或类对象(静态方法)。

显式锁 Lock 接口

Lock lock = new ReentrantLock();  // 可重入锁
lock.lock();
try { /* 临界区代码 */ } finally { lock.unlock(); }  

读写锁 ReentrantReadWriteLock

ReadWriteLock rwLock = new ReentrantReadWriteLock();
rwLock.readLock().lock();  // 读锁(共享)
rwLock.writeLock().lock(); // 写锁(独占)

5.锁的分类

  • 悲观锁:认为并发冲突概率高,操作前先加锁(如 synchronizedReentrantLock

  • 乐观锁:假设无并发冲突,通过版本号或 CAS(如 AtomicInteger)实现

  • 独享锁(排他锁):同一时刻仅允许一个线程访问资源(如 ReentrantLocksynchronized

  • 共享锁:允许多个线程同时访问资源(如 ReadWriteLock 的读锁,读共享,写独占)

  • 公平锁:按线程申请顺序获取锁(如 ReentrantLock(true)

  • 非公平锁:允许插队获取锁,吞吐量更高(如 synchronizedReentrantLock 默认模式)

  • 可重入锁:同一线程可重复获取同一锁(如 synchronizedReentrantLock

  • 不可重入锁:线程需释放锁后才能重新获取(如早期 Lock 实现,现较少见)

6.CAS自旋锁

在 Java 中,CAS(Compare And Swap)锁是一种基于乐观锁的无锁并发机制,通过原子操作实现线程安全。

1.使用 Atomic 原子类

AtomicInteger atomicInt = new AtomicInteger(0);
atomicInt.compareAndSet(0, 1); // 如果当前值为0,则更新为1

2.自旋锁实现

public class SpinLock {
    private AtomicBoolean locked = new AtomicBoolean(false);

    public void lock() {
        while (!locked.compareAndSet(false, true)) {
            // 自旋等待,直到获取锁成功
        }
    }

    public void unlock() {
        locked.set(false);
    }
}
类/接口 说明 示例方法
AtomicInteger 对整型变量进行原子操作 getAndIncrement()compareAndSet()
AtomicReference 对引用类型变量进行原子操作 compareAndSet()getAndUpdate()
AtomicStampedReference 解决 ABA 问题(通过版本号控制) compareAndSet(expected, new, stamp)

注意事项与局限性

  1. ABA 问题
    若变量值从 A 变为 B 再变回 A,CAS 会误认为未变化。解决方法:
    • 使用 AtomicStampedReference 记录版本号。
    • 引入时间戳或递增计数器。
  2. 自旋开销
    CAS 在竞争激烈时会导致长时间自旋,浪费 CPU 资源。适用场景建议:
    • 并发冲突概率较低的场景25。
    • 替代传统的 synchronized 锁(如轻量级并发场景)。
  3. 仅支持单一变量
    CAS 只能保证单个变量的原子性,多变量操作需结合其他同步机制(如锁)。

7.ReentrantLock

ReentrantLock 是 Java 并发编程中实现 Lock 接口的可重入锁,基于 AQS(AbstractQueuedSynchronizer) 框架实现,提供比 synchronized 更灵活的锁控制机制。

核心特性

  • 可重入性
    同一线程可多次获取同一锁,通过递增 state 变量实现(如递归调用场景)。
  • 公平锁与非公平锁
    • 非公平锁(默认):允许线程抢占锁,性能更高但可能导致饥饿。
    • 公平锁:按线程请求顺序分配锁,遵循 FIFO 原则。
  • 可中断与超时机制
    • lockInterruptibly() 支持中断等待中的线程。
    • tryLock(long timeout, TimeUnit unit) 允许在指定时间内尝试获取锁。
Lock lock = new ReentrantLock();          // 默认非公平锁
Lock fairLock = new ReentrantLock(true);  // 公平锁
lock.lock();
try {
    // 临界区操作
} finally {
    lock.unlock();  // 必须显式释放锁
}
特性 ReentrantLock synchronized
锁获取方式 显式调用 lock()/unlock() 隐式通过代码块或方法
公平性 支持公平与非公平模式 仅非公平锁
中断响应 支持(lockInterruptibly() 不支持
条件队列 多个 Condition 对象 单一等待队列
性能 高并发场景下更优(非公平锁) 较低版本 JDK 性能较差

实现原理

  • AQS 框架
    通过内部类 Sync 继承 AbstractQueuedSynchronizer,利用 state 变量记录锁状态和重入次数。
    • NonfairSync:非公平锁实现,直接尝试 CAS 抢占锁。
    • FairSync:公平锁实现,先检查队列中是否有等待线程。
  • 锁释放逻辑
    每次 unlock() 调用递减 state,当 state=0 时唤醒等待线程
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容