云计算百科
云计算领域专业知识百科平台

Java并发编程基础解析

一、引言

在当今计算机硬件飞速发展的背景下,多核处理器已经成为标准配置。如何充分利用多核CPU的计算能力,提高应用程序的性能和响应速度,成为每个Java开发者必须掌握的核心技能。并发编程作为提升程序性能的重要手段,在Java生态系统中占据着重要地位。

Java从诞生之初就将多线程支持作为核心特性之一,提供了丰富的并发编程API和工具类。从基础的Thread类到高级的并发集合,从简单的同步机制到复杂的并发框架,Java为开发者构建高性能并发应用提供了完整的技术栈。掌握Java并发编程不仅是技术深度的体现,更是解决实际性能问题的关键能力。

二、核心概念

2.1 进程与线程

进程是操作系统资源分配的基本单位,每个进程都有独立的内存空间和系统资源。一个应用程序可以包含多个进程。

线程是CPU调度的基本单位,也被称为轻量级进程。同一进程内的线程共享进程的内存空间和资源,但拥有独立的程序计数器、栈和局部变量。

// 进程与线程的区别示例
public class ProcessThreadDemo {
public static void main(String[] args) {
// main方法本身就是一个线程
System.out.println("主线程名称: " + Thread.currentThread().getName());

// 创建并启动新线程
Thread newThread = new Thread(() -> {
System.out.println("新线程名称: " + Thread.currentThread().getName());
});
newThread.start();
}
}

2.2 并发与并行

并发是指多个任务在同一个时间段内交替执行,宏观上看起来是同时进行,但微观上是交替使用CPU资源。

并行是指多个任务真正在同一时刻执行,需要多核CPU支持。

并发模式(单核CPU):
时间片1: 任务A
时间片2: 任务B
时间片3: 任务A
时间片4: 任务B

并行模式(多核CPU):
核心1: 任务A (持续执行)
核心2: 任务B (持续执行)

三、Java并发基础

3.1 Thread类与Runnable接口

Java提供了两种主要的线程创建方式:继承Thread类和实现Runnable接口。

// 方式一:继承Thread类
class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + ": " + i);
try {
Thread.sleep(500); // 线程休眠500毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

// 方式二:实现Runnable接口
class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + ": " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

public class ThreadCreationDemo {
public static void main(String[] args) {
// 使用Thread类创建线程
MyThread thread1 = new MyThread();
thread1.setName("Thread-方式一");
thread1.start(); // 启动线程,不要直接调用run()方法

// 使用Runnable接口创建线程
MyRunnable runnable = new MyRunnable();
Thread thread2 = new Thread(runnable);
thread2.setName("Thread-方式二");
thread2.start();

// 使用Lambda表达式简化Runnable实现(推荐)
Thread thread3 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println("Lambda线程: " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread3.start();
}
}

3.2 线程生命周期及状态转换

Java线程有6种状态,通过Thread.State枚举定义:

  • NEW(新建) :线程被创建但未启动
  • RUNNABLE(可运行) :线程正在JVM中执行,可能在等待CPU资源
  • BLOCKED(阻塞) :线程等待监视器锁进入同步块/方法
  • WAITING(等待) :线程无限期等待其他线程执行特定操作
  • TIMED_WAITING(计时等待) :线程等待其他线程执行操作,但有超时时间
  • TERMINATED(终止) :线程执行完毕
  • public class ThreadStateDemo {
    public static void main(String[] args) throws InterruptedException {
    Thread thread = new Thread(() -> {
    try {
    // TIMED_WAITING状态
    Thread.sleep(1000);
    synchronized (ThreadStateDemo.class) {
    // BLOCKED状态(等待锁)
    Thread.sleep(100);
    }
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    });

    System.out.println("线程状态: " + thread.getState()); // NEW

    thread.start();
    System.out.println("线程状态: " + thread.getState()); // RUNNABLE

    Thread.sleep(100);
    System.out.println("线程状态: " + thread.getState()); // TIMED_WAITING

    thread.join();
    System.out.println("线程状态: " + thread.getState()); // TERMINATED
    }
    }

    3.3 线程创建方式对比

    特性继承Thread类实现Runnable接口
    单继承限制 ✗ 只能继承一个类 ✓ 可实现多个接口
    代码耦合度 高(业务逻辑与线程耦合) 低(业务逻辑独立)
    资源共享 困难 容易(适合共享资源场景)
    推荐使用 不推荐 推荐

    四、线程同步机制

    4.1 synchronized关键字详解

    synchronized是Java中最基本的同步机制,用于解决多线程访问共享资源的线程安全问题。

    4.1.1 同步方法

    public class SynchronizedMethodDemo {
    private int counter = 0;

    // 同步实例方法
    public synchronized void increment() {
    counter++;
    }

    // 同步静态方法
    public static synchronized void staticMethod() {
    System.out.println("同步静态方法");
    }

    public static void main(String[] args) throws InterruptedException {
    SynchronizedMethodDemo demo = new SynchronizedMethodDemo();

    // 创建多个线程并发修改counter
    Thread[] threads = new Thread[10];
    for (int i = 0; i < threads.length; i++) {
    threads[i] = new Thread(() -> {
    for (int j = 0; j < 1000; j++) {
    demo.increment();
    }
    });
    threads[i].start();
    }

    // 等待所有线程执行完成
    for (Thread thread : threads) {
    thread.join();
    }

    System.out.println("最终counter值: " + demo.counter); // 应该输出10000
    }
    }

    4.1.2 同步代码块

    public class SynchronizedBlockDemo {
    private int counter = 0;
    private final Object lock = new Object(); // 专用锁对象

    // 使用synchronized代码块
    public void increment() {
    synchronized (lock) { // 使用专用锁对象
    counter++;
    }
    }

    // 同步this对象
    public void incrementThis() {
    synchronized (this) {
    counter++;
    }
    }

    // 同步Class对象
    public static void staticBlockMethod() {
    synchronized (SynchronizedBlockDemo.class) {
    System.out.println("同步代码块 – Class对象锁");
    }
    }

    public static void main(String[] args) throws InterruptedException {
    SynchronizedBlockDemo demo = new SynchronizedBlockDemo();

    Runnable task = () -> {
    for (int i = 0; i < 1000; i++) {
    demo.increment();
    }
    };

    Thread thread1 = new Thread(task);
    Thread thread2 = new Thread(task);

    thread1.start();
    thread2.start();

    thread1.join();
    thread2.join();

    System.out.println("Counter值: " + demo.counter); // 应该输出2000
    }
    }

    4.2 volatile关键字

    volatile关键字用于确保变量的可见性和有序性,但不能保证原子性。

    public class VolatileDemo {
    // 使用volatile保证可见性
    private volatile boolean running = true;
    private volatile int counter = 0;

    public void stop() {
    running = false;
    }

    public void startThread() {
    Thread worker = new Thread(() -> {
    while (running) {
    counter++;
    try {
    Thread.sleep(100);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }
    System.out.println("线程已停止,counter值: " + counter);
    });

    worker.start();

    // 主线程休眠2秒后停止工作线程
    try {
    Thread.sleep(2000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    stop();
    }

    public static void main(String[] args) {
    VolatileDemo demo = new VolatileDemo();
    demo.startThread();
    }
    }

    4.3 线程安全问题及解决方案

    4.3.1 线程安全问题产生原因

  • 原子性问题:一个操作不能被中断,要么全部执行,要么都不执行
  • 可见性问题:一个线程修改了共享变量的值,其他线程不能立即看到
  • 有序性问题:指令重排序导致代码执行顺序与预期不符
  • 4.3.2 线程安全示例与解决方案

    public class ThreadSafetyDemo {
    // 问题示例:非线程安全的计数器
    private int unsafeCounter = 0;

    public void unsafeIncrement() {
    unsafeCounter++; // 非原子操作,存在线程安全问题
    }

    // 解决方案1:使用synchronized
    private int syncCounter = 0;

    public synchronized void syncIncrement() {
    syncCounter++;
    }

    // 解决方案2:使用Atomic类(推荐)
    private AtomicInteger atomicCounter = new AtomicInteger(0);

    public void atomicIncrement() {
    atomicCounter.incrementAndGet();
    }

    // 解决方案3:使用ReentrantLock
    private int lockCounter = 0;
    private ReentrantLock lock = new ReentrantLock();

    public void lockIncrement() {
    lock.lock();
    try {
    lockCounter++;
    } finally {
    lock.unlock(); // 必须在finally中释放锁
    }
    }

    public static void main(String[] args) throws InterruptedException {
    ThreadSafetyDemo demo = new ThreadSafetyDemo();
    int threadCount = 100;
    int incrementPerThread = 1000;

    // 测试非线程安全版本
    testUnsafe(demo, threadCount, incrementPerThread);

    // 测试Atomic版本
    testAtomic(demo, threadCount, incrementPerThread);
    }

    private static void testUnsafe(ThreadSafetyDemo demo, int threadCount, int incrementPerThread)
    throws InterruptedException {
    demo.unsafeCounter = 0;
    Thread[] threads = new Thread[threadCount];

    long startTime = System.currentTimeMillis();
    for (int i = 0; i < threadCount; i++) {
    threads[i] = new Thread(() -> {
    for (int j = 0; j < incrementPerThread; j++) {
    demo.unsafeIncrement();
    }
    });
    threads[i].start();
    }

    for (Thread thread : threads) {
    thread.join();
    }

    long endTime = System.currentTimeMillis();
    System.out.println("非线程安全 – 结果: " + demo.unsafeCounter +
    ", 预期: " + (threadCount * incrementPerThread) +
    ", 耗时: " + (endTime – startTime) + "ms");
    }

    private static void testAtomic(ThreadSafetyDemo demo, int threadCount, int incrementPerThread)
    throws InterruptedException {
    demo.atomicCounter.set(0);
    Thread[] threads = new Thread[threadCount];

    long startTime = System.currentTimeMillis();
    for (int i = 0; i < threadCount; i++) {
    threads[i] = new Thread(() -> {
    for (int j = 0; j < incrementPerThread; j++) {
    demo.atomicIncrement();
    }
    });
    threads[i].start();
    }

    for (Thread thread : threads) {
    thread.join();
    }

    long endTime = System.currentTimeMillis();
    System.out.println("Atomic类 – 结果: " + demo.atomicCounter.get() +
    ", 预期: " + (threadCount * incrementPerThread) +
    ", 耗时: " + (endTime – startTime) + "ms");
    }
    }

    五、并发工具类

    5.1 ThreadPoolExecutor线程池

    线程池能够有效管理线程资源,提高程序性能。

    import java.util.concurrent.*;
    import java.util.concurrent.atomic.AtomicInteger;

    public class ThreadPoolDemo {
    private static final AtomicInteger taskNumber = new AtomicInteger(1);

    public static void main(String[] args) {
    // 创建线程池
    ThreadPoolExecutor executor = new ThreadPoolExecutor(
    2, // 核心线程数
    4, // 最大线程数
    60L, // 空闲线程存活时间
    TimeUnit.SECONDS, // 时间单位
    new ArrayBlockingQueue<>(10), // 任务队列
    Executors.defaultThreadFactory(), // 线程工厂
    new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
    );

    // 提交任务
    for (int i = 0; i < 15; i++) {
    final int taskId = taskNumber.getAndIncrement();
    executor.submit(() -> {
    System.out.println("任务" + taskId + "开始执行 – " +
    Thread.currentThread().getName());
    try {
    Thread.sleep(1000); // 模拟任务执行
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println("任务" + taskId + "执行完成");
    return "任务" + taskId + "的结果";
    });
    }

    // 关闭线程池
    executor.shutdown();
    try {
    // 等待所有任务完成
    if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
    executor.shutdownNow();
    }
    } catch (InterruptedException e) {
    executor.shutdownNow();
    }
    }
    }

    5.2 CountDownLatch倒计时门栓

    CountDownLatch用于协调多个线程之间的执行顺序。

    import java.util.concurrent.*;
    import java.util.concurrent.atomic.AtomicInteger;

    public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
    final int THREAD_COUNT = 5;
    CountDownLatch startLatch = new CountDownLatch(1); // 开始信号
    CountDownLatch endLatch = new CountDownLatch(THREAD_COUNT); // 结束信号
    AtomicInteger resultSum = new AtomicInteger(0);

    // 创建并启动工作线程
    for (int i = 1; i <= THREAD_COUNT; i++) {
    final int threadId = i;
    new Thread(() -> {
    try {
    System.out.println("线程" + threadId + "准备就绪,等待开始信号…");
    startLatch.await(); // 等待开始信号

    System.out.println("线程" + threadId + "开始执行任务");
    int result = threadId * threadId; // 模拟计算任务
    Thread.sleep(1000); // 模拟耗时操作

    resultSum.addAndGet(result);
    System.out.println("线程" + threadId + "执行完成,结果: " + result);

    } catch (InterruptedException e) {
    e.printStackTrace();
    } finally {
    endLatch.countDown(); // 完成后倒计时
    }
    }).start();
    }

    // 主线程等待所有线程准备就绪
    Thread.sleep(100);
    System.out.println("主线程:所有工作线程准备就绪,发送开始信号!");
    startLatch.countDown(); // 发送开始信号

    // 等待所有工作线程完成
    endLatch.await();
    System.out.println("主线程:所有任务执行完成,总和: " + resultSum.get());
    }
    }

    5.3 其他常用工具类简介

    • Semaphore(信号量) :控制同时访问特定资源的线程数量
    • CyclicBarrier(循环栅栏) :让一组线程到达一个屏障时被阻塞,直到最后一个线程到达屏障时,所有被屏障拦截的线程才会继续执行
    • ConcurrentHashMap:线程安全的HashMap实现
    • BlockingQueue:阻塞队列,支持线程安全的生产-消费模式

    六、实践建议

    6.1 并发编程常见问题及避坑指南

    6.1.1 死锁问题

    public class DeadlockDemo {
    private static final Object lock1 = new Object();
    private static final Object lock2 = new Object();

    public static void main(String[] args) {
    Thread thread1 = new Thread(() -> {
    synchronized (lock1) {
    System.out.println("线程1获取lock1");
    try { Thread.sleep(100); } catch (InterruptedException e) {}
    System.out.println("线程1等待lock2");
    synchronized (lock2) {
    System.out.println("线程1获取lock2");
    }
    }
    });

    Thread thread2 = new Thread(() -> {
    synchronized (lock2) {
    System.out.println("线程2获取lock2");
    try { Thread.sleep(100); } catch (InterruptedException e) {}
    System.out.println("线程2等待lock1");
    synchronized (lock1) {
    System.out.println("线程2获取lock1");
    }
    }
    });

    thread1.start();
    thread2.start();
    // 可能产生死锁
    }
    }

    避坑建议:

    • 避免嵌套锁
    • 按照固定顺序获取锁
    • 设置锁超时时间
    • 使用tryLock()替代lock()
    6.1.2 资源泄漏

    // 错误示例:线程池未正确关闭
    public class ResourceLeakDemo {
    public static void wrongExample() {
    ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);
    executor.submit(() -> System.out.println("任务执行"));
    // 忘记调用shutdown(),导致线程池无法关闭
    }

    // 正确示例
    public static void correctExample() {
    ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);
    try {
    executor.submit(() -> System.out.println("任务执行"));
    } finally {
    executor.shutdown(); // 确保线程池被关闭
    }
    }
    }

    6.2 性能优化方向

  • 合理使用线程池:根据任务类型选择合适的线程池参数
  • 减少锁粒度:使用细粒度锁代替粗粒度锁
  • 使用无锁编程:优先使用Atomic类和并发集合
  • 避免上下文切换:减少线程创建和销毁开销
  • 合理设置线程数量:通常设置为CPU核心数的1-2倍
  • import java.util.concurrent.*;
    import java.util.concurrent.atomic.AtomicLong;

    public class PerformanceOptimizationDemo {
    // 性能对比:同步方式 vs 无锁方式
    private long syncCounter = 0;
    private AtomicLong atomicCounter = new AtomicLong(0);

    // 同步方式
    public synchronized void syncIncrement() {
    syncCounter++;
    }

    // 无锁方式
    public void atomicIncrement() {
    atomicCounter.incrementAndGet();
    }

    public static void main(String[] args) throws InterruptedException {
    PerformanceOptimizationDemo demo = new PerformanceOptimizationDemo();
    int threadCount = 10;
    int operations = 1000000;

    // 测试synchronized性能
    long syncStart = System.currentTimeMillis();
    testSyncMethod(demo, threadCount, operations);
    long syncEnd = System.currentTimeMillis();

    // 测试Atomic性能
    long atomicStart = System.currentTimeMillis();
    testAtomicMethod(demo, threadCount, operations);
    long atomicEnd = System.currentTimeMillis();

    System.out.println("synchronized耗时: " + (syncEnd – syncStart) + "ms");
    System.out.println("Atomic耗时: " + (atomicEnd – atomicStart) + "ms");
    }

    private static void testSyncMethod(PerformanceOptimizationDemo demo,
    int threadCount, int operations)
    throws InterruptedException {
    demo.syncCounter = 0;
    CountDownLatch latch = new CountDownLatch(threadCount);

    for (int i = 0; i < threadCount; i++) {
    new Thread(() -> {
    for (int j = 0; j < operations; j++) {
    demo.syncIncrement();
    }
    latch.countDown();
    }).start();
    }

    latch.await();
    }

    private static void testAtomicMethod(PerformanceOptimizationDemo demo,
    int threadCount, int operations)
    throws InterruptedException {
    demo.atomicCounter.set(0);
    CountDownLatch latch = new CountDownLatch(threadCount);

    for (int i = 0; i < threadCount; i++) {
    new Thread(() -> {
    for (int j = 0; j < operations; j++) {
    demo.atomicIncrement();
    }
    latch.countDown();
    }).start();
    }

    latch.await();
    }
    }

    七、总结

    Java并发编程是构建高性能应用的重要技术,掌握其核心概念和实践技巧对开发者至关重要。本文从并发编程的基础概念入手,详细介绍了Java中的线程创建、同步机制、并发工具类等核心内容,并提供了丰富的代码示例和实践建议。

    核心知识点回顾:

  • 进程与线程:理解进程和线程的区别,掌握并发与并行的概念
  • 线程创建:优先使用Runnable接口,避免继承Thread类的单继承限制
  • 同步机制:合理使用synchronized和volatile,理解其底层原理
  • 线程安全:识别线程安全问题,选择合适的解决方案(Atomic类、Lock等)
  • 并发工具:熟练使用线程池、CountDownLatch等并发工具提高程序性能
  • 性能优化:通过减少锁粒度、使用无锁编程等方式优化并发性能
  • Java并发编程的发展展望:

    随着Java版本的不断更新,并发编程API也在持续优化。Java 8引入的CompletableFuture大大简化了异步编程,Java 9的响应式编程支持为并发编程带来了新的可能性。未来,Java并发编程将更加注重性能优化、易用性和可维护性,开发者需要持续学习和掌握新的并发编程技术。

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » Java并发编程基础解析
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!