并发编程(五)

并发编程基础

并发与并行的区别

并发(Concurrency)指多个任务在单核CPU上通过时间片轮转交替执行,宏观上看似同时运行,微观实为串行。并行(Parallelism)则是真正的多任务同时执行,需多核CPU或分布式系统支持。例如,单核CPU的多线程是并发,多核CPU的多线程可实现真正并行。

线程与进程的概念

进程是操作系统资源分配的基本单位,拥有独立的地址空间和系统资源。线程是CPU调度的基本单位,同进程内的多线程共享进程资源(内存、文件描述符等),但各自维护程序计数器、栈和局部变量。

Java线程的生命周期及状态转换

Java线程包含六种状态:

  • NEW:新建状态,线程已创建未启动
  • RUNNABLE:可运行状态(包含就绪READY和运行中RUNNING)
  • BLOCKED:阻塞状态,等待获取监视器锁
  • WAITING:等待状态,需其他线程显式唤醒
  • TIMED_WAITING:超时等待状态,定时自动唤醒
  • TERMINATED:终止状态,线程执行完毕

状态转换路径:

  • NEW → RUNNABLE:调用start()
  • RUNNABLE → BLOCKED:等待锁
  • RUNNABLE → WAITING:调用wait()/join()/park()
  • RUNNABLE → TIMED_WAITING:调用sleep(timeout)等
  • 阻塞/等待状态 → RUNNABLE:获取锁/被唤醒/超时

线程创建方式

继承Thread类

java 复制代码
class MyThread extends Thread {
    @Override
    public void run() {
        // 线程逻辑
    }
}
MyThread t = new MyThread();
t.start();

实现Runnable接口

java 复制代码
class MyRunnable implements Runnable {
    @Override
    public void run() {
        // 线程逻辑
    }
}
Thread t = new Thread(new MyRunnable());
t.start();

Callable与Future

java 复制代码
class MyCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        // 线程逻辑
        return "result";
    }
}
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> future = executor.submit(new MyCallable());
String result = future.get(); // 阻塞获取结果

线程安全与同步机制详解

线程安全问题根源

原子性问题详解

原子性问题指的是一个操作不可分割的特性被破坏。例如i++操作实际上包含三个步骤:

  1. 读取变量i的值到工作内存
  2. 在工作内存中进行加1运算
  3. 将结果写回主内存

在多线程环境下,如果两个线程同时执行i++操作,可能会出现如下时序问题:

复制代码
线程A:读取i=0 → 计算i=1 → 写回i=1
线程B:读取i=0 → 计算i=1 → 写回i=1

最终结果i=1而不是预期的2,这就是典型的原子性问题。

可见性问题详解

可见性问题源于JMM的内存模型设计。每个线程有自己的工作内存,修改共享变量后不会立即同步到主内存。例如:

java 复制代码
// 线程A
sharedFlag = true;

// 线程B
while(!sharedFlag) {
    // 可能陷入死循环,即使线程A已经修改了sharedFlag
}

有序性问题详解

处理器和编译器会对指令进行重排序优化。例如:

java 复制代码
// 初始状态
int a = 0;
boolean flag = false;

// 线程A
a = 1;          // 语句1
flag = true;    // 语句2

// 线程B
if(flag) {      // 语句3
    int i = a;  // 语句4
}

由于指令重排序,线程A可能先执行语句2再执行语句1,导致线程B读取到a=0。

synchronized关键字深入

实现原理详解

  • Monitor对象:每个Java对象都与一个Monitor关联,包含:

    • _owner:持有锁的线程
    • _EntryList:等待获取锁的线程队列
    • _WaitSet:调用wait()的线程集合
  • 同步方法:编译后在方法访问标志添加ACC_SYNCHRONIZED,方法调用时自动获取对象的Monitor

  • 同步代码块

    • monitorenter:尝试获取Monitor所有权
    • monitorexit:释放Monitor所有权
    • 编译器自动添加异常处理确保锁释放

锁升级过程(JDK6+)

  1. 无锁状态
  2. 偏向锁:记录线程ID,减少CAS开销
  3. 轻量级锁:通过CAS竞争锁
  4. 重量级锁:真正的Monitor竞争

volatile关键字详解

底层实现

  • 写操作:加入StoreStore屏障 + StoreLoad屏障
  • 读操作:加入LoadLoad屏障 + LoadStore屏障

典型应用场景

  1. 状态标志位:
java 复制代码
volatile boolean shutdownRequested;

public void shutdown() { shutdownRequested = true; }

public void doWork() {
    while(!shutdownRequested) {
        // 执行任务
    }
}
  1. 单例模式双重检查锁定:
java 复制代码
class Singleton {
    private volatile static Singleton instance;
    
    public static Singleton getInstance() {
        if(instance == null) {
            synchronized(Singleton.class) {
                if(instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

锁分类详解

悲观锁实现对比

特性 synchronized ReentrantLock
实现机制 JVM内置 JDK实现
可中断 不可
公平锁 非公平 可选
条件变量 单一 多条件
锁降级 不支持 不支持

乐观锁实现方式

  1. CAS原语:
java 复制代码
AtomicInteger atomicInt = new AtomicInteger(0);
atomicInt.incrementAndGet();
  1. 版本号机制:
sql 复制代码
UPDATE table SET value=newValue, version=version+1 
WHERE id=#{id} AND version=#{oldVersion}

Java并发工具类详解

ReentrantLock高级特性

  1. 可中断锁获取:
java 复制代码
lock.lockInterruptibly();
try {
    // 临界区
} finally {
    lock.unlock();
}
  1. 尝试获取锁:
java 复制代码
if(lock.tryLock(1, TimeUnit.SECONDS)) {
    try {
        // 临界区
    } finally {
        lock.unlock();
    }
} else {
    // 超时处理
}
  1. 公平锁与非公平锁:
java 复制代码
// 公平锁(按申请顺序获取)
ReentrantLock fairLock = new ReentrantLock(true);

// 非公平锁(可能插队)
ReentrantLock unfairLock = new ReentrantLock(false);

读写锁应用场景

适合读多写少场景,如:

  • 缓存系统
  • 配置信息管理
  • 数据看板

典型使用模式:

java 复制代码
class CachedData {
    final ReadWriteLock rwl = new ReentrantReadWriteLock();
    Object data;
    boolean cacheValid;
    
    void processCachedData() {
        rwl.readLock().lock();
        if(!cacheValid) {
            rwl.readLock().unlock();
            rwl.writeLock().lock();
            try {
                if(!cacheValid) {
                    data = fetchDataFromDB();
                    cacheValid = true;
                }
                rwl.readLock().lock(); // 锁降级
            } finally {
                rwl.writeLock().unlock();
            }
        }
        try {
            use(data);
        } finally {
            rwl.readLock().unlock();
        }
    }
}

条件变量典型应用

  1. 生产者-消费者模型:
java 复制代码
class BoundedBuffer {
    final Lock lock = new ReentrantLock();
    final Condition notFull = lock.newCondition();
    final Condition notEmpty = lock.newCondition();
    final Object[] items = new Object[100];
    int putptr, takeptr, count;
    
    public void put(Object x) throws InterruptedException {
        lock.lock();
        try {
            while(count == items.length)
                notFull.await();
            items[putptr] = x;
            if(++putptr == items.length) putptr = 0;
            ++count;
            notEmpty.signal();
        } finally {
            lock.unlock();
        }
    }
    
    public Object take() throws InterruptedException {
        lock.lock();
        try {
            while(count == 0)
                notEmpty.await();
            Object x = items[takeptr];
            if(++takeptr == items.length) takeptr = 0;
            --count;
            notFull.signal();
            return x;
        } finally {
            lock.unlock();
        }
    }
}
  1. 线程交替执行:
java 复制代码
class AlternatingThreads {
    Lock lock = new ReentrantLock();
    Condition conditionA = lock.newCondition();
    Condition conditionB = lock.newCondition();
    boolean turnA = true;
    
    void threadA() {
        lock.lock();
        try {
            while(!turnA) {
                conditionA.await();
            }
            // 执行A的工作
            turnA = false;
            conditionB.signal();
        } finally {
            lock.unlock();
        }
    }
    
    void threadB() {
        lock.lock();
        try {
            while(turnA) {
                conditionB.await();
            }
            // 执行B的工作
            turnA = true;
            conditionA.signal();
        } finally {
            lock.unlock();
        }
    }
}

并发容器详解

ConcurrentHashMap演进

  1. JDK7:分段锁机制

    • 将数据分为16个段(Segment)
    • 每个段独立加锁
    • 并发度=分段数
  2. JDK8+:

    • 改为CAS+synchronized实现
    • 数组+链表+红黑树结构
    • 锁粒度缩小到单个链表/树节点

CopyOnWriteArrayList适用场景

  • 监听器列表管理
  • 只读或极少修改的场景
  • 迭代频繁且不需要实时数据的场景

示例:

java 复制代码
class EventManager {
    private final CopyOnWriteArrayList<EventListener> listeners = new CopyOnWriteArrayList<>();
    
    public void addListener(EventListener listener) {
        listeners.add(listener);
    }
    
    public void removeListener(EventListener listener) {
        listeners.remove(listener);
    }
    
    public void fireEvent(Event event) {
        for(EventListener listener : listeners) {
            listener.onEvent(event);
        }
    }
}

阻塞队列对比

队列类型 特性 适用场景
ArrayBlockingQueue 有界、数组实现、公平可选 固定资源池
LinkedBlockingQueue 可选有界、链表实现 无界任务队列
PriorityBlockingQueue 无界、优先级排序 任务优先级调度
SynchronousQueue 无缓冲、直接传递 手递手任务传递
DelayQueue 无界、延迟元素 定时任务调度

线程池深度解析

核心参数配置建议

  1. corePoolSize:

    • CPU密集型:N_cpu + 1
    • IO密集型:N_cpu * 2
  2. maximumPoolSize:

    • 根据系统负载和资源限制设置
    • 一般不超过corePoolSize的2-3倍
  3. keepAliveTime:

    • 突发流量场景:设置较短时间(30-60秒)
    • 稳定流量场景:设置较长时间(5-10分钟)
  4. 工作队列选择:

    • 快速响应:SynchronousQueue
    • 平滑突发:LinkedBlockingQueue
    • 限流保护:ArrayBlockingQueue

拒绝策略对比

  1. AbortPolicy(默认):抛出RejectedExecutionException
  2. CallerRunsPolicy:由调用线程执行任务
  3. DiscardPolicy:静默丢弃任务
  4. DiscardOldestPolicy:丢弃队列最老任务并重试

自定义拒绝策略示例:

java 复制代码
ThreadPoolExecutor executor = new ThreadPoolExecutor(
    corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.SECONDS,
    new LinkedBlockingQueue<>(100),
    new RejectedExecutionHandler() {
        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            // 记录日志
            logger.warn("Task rejected: " + r.toString());
            // 持久化任务
            saveToDisk(r);
            // 重试机制
            if(!executor.isShutdown()) {
                executor.submit(r);
            }
        }
    });

线程池监控指标

  1. 核心指标:

    • 活动线程数:getActiveCount()
    • 任务队列大小:getQueue().size()
    • 已完成任务数:getCompletedTaskCount()
  2. 扩展监控示例:

java 复制代码
class MonitorThread implements Runnable {
    private final ThreadPoolExecutor executor;
    
    public MonitorThread(ThreadPoolExecutor executor) {
        this.executor = executor;
    }
    
    @Override
    public void run() {
        while(true) {
            System.out.println(
                String.format("[monitor] [%d/%d] Active: %d, Completed: %d, Task: %d, isShutdown: %s, isTerminated: %s",
                    executor.getPoolSize(),
                    executor.getCorePoolSize(),
                    executor.getActiveCount(),
                    executor.getCompletedTaskCount(),
                    executor.getTaskCount(),
                    executor.isShutdown(),
                    executor.isTerminated()));
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                break;
            }
        }
    }
}

CompletableFuture高级用法

组合操作

  1. thenCombine:合并两个独立Future的结果
java 复制代码
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "World");

future1.thenCombine(future2, (s1, s2) -> s1 + " " + s2)
       .thenAccept(System.out::println);
  1. allOf/anyOf:
java 复制代码
CompletableFuture<Void> all = CompletableFuture.allOf(future1, future2);
all.thenRun(() -> {
    // 所有future完成
});

CompletableFuture<Object> any = CompletableFuture.anyOf(future1, future2);
any.thenAccept(result -> {
    // 任意一个future完成
});

异常处理

  1. 多级异常处理:
java 复制代码
CompletableFuture.supplyAsync(() -> {
        // 可能抛出异常的操作
        return doSomething();
    })
    .thenApply(result -> {
        // 转换结果
        return process(result);
    })
    .exceptionally(ex -> {
        // 第一级异常处理
        logger.error("Stage1 error", ex);
        return defaultValue;
    })
    .thenAccept(result -> {
        // 消费结果
        consume(result);
    })
    .exceptionally(ex -> {
        // 第二级异常处理
        logger.error("Stage2 error", ex);
        return null;
    });
  1. handle方法统一处理:
java 复制代码
CompletableFuture.supplyAsync(() -> riskyOperation())
    .handle((result, ex) -> {
        if(ex != null) {
            // 异常处理
            return fallbackValue;
        }
        // 正常处理
        return result;
    });

Java内存模型(JMM)详解

内存屏障类型

  1. LoadLoad屏障:

    • 确保Load1数据的装载先于Load2及后续装载指令
    • 典型应用:volatile读操作后插入
  2. StoreStore屏障:

    • 确保Store1数据对其他处理器可见先于Store2及后续存储指令
    • 典型应用:volatile写操作前插入
  3. LoadStore屏障:

    • 确保Load1数据装载先于Store2及后续存储指令
    • 典型应用:volatile读操作后插入
  4. StoreLoad屏障:

    • 确保Store1数据对其他处理器可见先于Load2及后续装载指令
    • 开销最大的屏障,对应volatile写操作后插入

happens-before规则应用

  1. 程序顺序规则:
java 复制代码
int a = 1;  // 操作A
int b = 2;  // 操作B
// 操作A happens-before 操作B
  1. 监视器锁规则:
java 复制代码
// 线程A
synchronized(lock) {
    x = 1;  // 操作A
}

// 线程B
synchronized(lock) {
    int r = x; // 操作B
}
// 操作A happens-before 操作B
  1. volatile变量规则:
java 复制代码
// 线程A
volatileFlag = true;  // 操作A

// 线程B
if(volatileFlag) {    // 操作B
    // 操作A happens-before 操作B
}
  1. 线程启动规则:
java 复制代码
Thread t = new Thread(() -> {
    // 操作B
    doSomething();
});
// 操作A
t.start();
// 操作A happens-before 操作B
  1. 线程终止规则:
java 复制代码
Thread t = new Thread(() -> {
    // 操作A
    doSomething();
});
t.start();
t.join();
// 操作A happens-before 操作B
// 操作B
afterJoin();

final字段特殊规则

final字段的正确初始化保证:

java 复制代码
class FinalFieldExample {
    final int x;
    int y;
    static FinalFieldExample f;
    
    public FinalFieldExample() {
        x = 3;
        y = 4;
    }
    
    static void writer() {
        f = new FinalFieldExample();
    }
    
    static void reader() {
        if(f != null) {
            int i = f.x;  // 保证看到3
            int j = f.y;  // 可能看到0
        }
    }
}
相关推荐
过期动态1 小时前
【LeetCode 热题 100】无重复字符的最长子串
java·数据结构·spring boot·算法·leetcode·职场和发展
Yeats_Liao2 小时前
好复杂的 IoT 世界:工业数据采集技术栈全景解析
java·物联网·struts
月落归舟2 小时前
Java线程小记
java·开发语言
西凉的悲伤2 小时前
Spring Cloud Gateway介绍
java·spring cloud·gateway
逸Y 仙X2 小时前
文章五:Elasticsearch安全通信
java·大数据·安全·elasticsearch·搜索引擎·全文检索·jenkins
quan26312 小时前
20260529,日常开发-查老数据全量更新闭坑
java·mysql·主从·主从延迟
大大杰哥2 小时前
Java 日志框架详解:SLF4J + Logback 从入门到实战
java·开发语言·logback
Dest1ny-安全3 小时前
2026最新CTF知识库:12大Web漏洞深度文章+1156篇历年大赛WP+50+脚本+Payload速查 +AI/RAG离线在线知识库
java·学习·安全·web安全·servlet
404号扳手3 小时前
Java 基础知识(六)
java·后端