Java进程与线程及高并发实践指南

进程与线程的本质差异

(1)资源分配维度:
每个进程拥有独立的堆内存空间(JVM实例)、文件描述符和系统资源。例如在Java中通过ProcessBuilder创建子进程时,操作系统会分配约1MB的栈空间。而线程共享进程资源,仅保留独立栈(默认1MB,可通过-Xss调整)和程序计数器。

(2)执行调度成本:
上下文切换时,进程需要切换页表、刷新TLB,耗时约1-10微秒;线程切换仅需保存寄存器,耗时约0.1-1微秒。通过perf stat工具可观测到具体差异。

(3)通信机制对比:
进程间通信(IPC)常用方式:

// 命名管道示例
Path fifo = Paths.get("/tmp/myfifo");
Files.createNamedPipe(fifo, NamedPipeOptions.READ_WRITE);

// 共享内存示例
FileChannel channel = FileChannel.open(shmFile, READ, WRITE, CREATE, DELETE_ON_CLOSE);
MappedByteBuffer buffer = channel.map(READ_WRITE, 0, 1024);

线程间通信直接通过共享内存:

// 使用volatile保证可见性
volatile boolean flag = false;

// 使用Atomic原子类
AtomicInteger counter = new AtomicInteger(0);

线程实现深度解析

(1)JVM线程模型演进:

  • Green Thread(JDK 1.1):完全用户级线程,M:N映射
  • Native Thread(JDK 1.2起):1:1内核线程映射
  • Virtual Thread(Loom项目):M:N混合模型

(2)创建方式对比:

// 方法1:继承Thread(不推荐)
class MyThread extends Thread {
    public void run() {
        // 实现逻辑
    }
}

// 方法2:实现Runnable(推荐)
Runnable task = () -> {
    // 线程执行体
};
new Thread(task).start();

// 方法3:Callable + Future
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Integer> future = executor.submit(() -> {
    return 42;
});

(3)线程优先级误区:
通过setPriority(1-10)设置的优先级在Linux中被忽略,Windows中部分有效。建议使用CompletableFutureManagedTask进行精细控制。

线程状态转换全流程

stateDiagram-v2
    [*] --> NEW
    NEW --> RUNNABLE: start()
    RUNNABLE --> BLOCKED: 等待synchronized锁
    RUNNABLE --> WAITING: wait()/join()
    RUNNABLE --> TIMED_WAITING: sleep(n)
    BLOCKED --> RUNNABLE: 获取锁
    WAITING --> RUNNABLE: notify()/notifyAll()
    TIMED_WAITING --> RUNNABLE: 超时结束
    RUNNABLE --> TERMINATED: run()结束

通过jstack工具可查看实际线程状态:

$ jstack <pid> | grep 'java.lang.Thread.State'

线程池核心技术参数

(1)核心参数关系:

ThreadPoolExecutor(
    int corePoolSize,    // 常驻线程数
    int maximumPoolSize, // 最大扩容数
    long keepAliveTime,  // 空闲线程存活时间
    TimeUnit unit,
    BlockingQueue<Runnable> workQueue, // 任务队列
    RejectedExecutionHandler handler   // 拒绝策略
)

(2)队列类型对比表: | 队列类型 | 特性 | 适用场景 | |-----------------------|----------------------------|---------------------| | SynchronousQueue | 无容量,直接传递 | CachedThreadPool | | LinkedBlockingQueue | 无界队列(默认Integer.MAX_VALUE) | FixedThreadPool | | ArrayBlockingQueue | 有界队列 | 自定义线程池 | | PriorityBlockingQueue | 带优先级队列 | 任务需要优先级排序 | | DelayedWorkQueue | 延迟任务队列 | ScheduledThreadPool |

(3)拒绝策略源码分析:

// 默认四种策略
public static class AbortPolicy implements RejectedExecutionHandler {
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        throw new RejectedExecutionException();
    }
}

// 自定义策略示例
new ThreadPoolExecutor.CallerRunsPolicy() {
    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        if (!executor.isShutdown()) {
            r.run();  // 由提交线程直接执行
        }
    }
};

同步机制的实现原理

(1)synchronized底层演进:

  • JDK 1.6前:直接使用操作系统互斥锁
  • 偏向锁(Biased Locking):无竞争时直接进入
  • 轻量级锁(Thin Locking):CAS自旋尝试
  • 重量级锁:最终回退到操作系统互斥量

查看锁升级过程:

$ java -XX:+PrintFlagsFinal | grep BiasedLocking

(2)AQS(AbstractQueuedSynchronizer)原理:
ReentrantLock内部通过CLH队列实现:

// 简化版AQS获取锁逻辑
final void lock() {
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    else
        acquire(1);
}

protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        if (!hasQueuedPredecessors() &&
            compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    // 处理重入逻辑...
}

(3)内存屏障实现:
volatile变量写操作插入StoreStore + StoreLoad屏障:

; x86实现(内存屏障类型)
lock addl $0x0,(%rsp)

并发工具高级用法

(1)CountDownLatch与CyclicBarrier对比:

// CountDownLatch(一次性)
CountDownLatch latch = new CountDownLatch(3);
latch.countDown();
latch.await();

// CyclicBarrier(可重复使用)
CyclicBarrier barrier = new CyclicBarrier(3, () -> 
    System.out.println("All parties arrived"));
barrier.await();

(2)CompletableFuture组合操作:

CompletableFuture.supplyAsync(() -> fetchData())
    .thenApplyAsync(data -> process(data))
    .exceptionally(ex -> handleError(ex))
    .thenAcceptAsync(result -> saveResult(result))
    .join();

(3)StampedLock优化读场景:

StampedLock lock = new StampedLock();

// 乐观读
long stamp = lock.tryOptimisticRead();
if (!lock.validate(stamp)) {
    stamp = lock.readLock();
    try {
        // 重新读取
    } finally {
        lock.unlockRead(stamp);
    }
}

虚拟线程实践指南

(1)创建方式对比:

// 平台线程
Thread.ofPlatform().name("worker-", 0).start(task);

// 虚拟线程
Thread.ofVirtual().name("vt-", 0).start(task);

// 使用ExecutorService
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    executor.submit(task1);
    executor.submit(task2);
}

(2)性能测试数据: | 线程类型 | 内存占用 | 创建时间 | 上下文切换成本 | |---------|------|-------|---------| | 平台线程 | 1MB | ~2ms | 高 | | 虚拟线程 | ~4KB | ~0.1ms | 极低 |

(3)注意事项:

  • 避免使用线程局部变量(ThreadLocal)
  • 不要包装在池中(本身就是轻量级)
  • 同步代码块仍会绑定到载体线程

生产环境问题排查

(1)死锁检测:

$ jstack <pid> | grep -A 10 deadlock

(2)线程泄漏检测:

// 使用ThreadMXBean
ThreadMXBean bean = ManagementFactory.getThreadMXBean();
long[] threadIds = bean.findDeadlockedThreads();
if (threadIds != null) {
    ThreadInfo[] infos = bean.getThreadInfo(threadIds);
    // 分析线程堆栈
}

(3)内存可见性问题诊断: 使用JFR(Java Flight Recorder)记录内存屏障事件:

$ jcmd <pid> JFR.start duration=60s filename=recording.jfr
正文到此结束
评论插件初始化中...
Loading...