Java 线程并发实战指南:使用方法与常见坑点

文章主题:Java 并发编程实战
目标读者:Java 开发者
内容特点:实战教程、代码示例、最佳实践

引言

在 Java 开发中,多线程编程是提升应用性能的关键技术。然而,并发编程也带来了许多挑战,如线程安全问题、死锁、内存可见性等。本文将深入讲解 Java 线程并发的常用方法、最佳实践以及常见坑点。


一、Java 线程创建方式

1. 继承 Thread 类

public class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("Thread running: " + Thread.currentThread().getName());
    }
}

// 启动线程
MyThread thread = new MyThread();
thread.start();

2. 实现 Runnable 接口

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("Runnable running: " + Thread.currentThread().getName());
    }
}

// 启动线程
Thread thread = new Thread(new MyRunnable());
thread.start();

3. 实现 Callable 接口(带返回值)

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

public class MyCallable implements Callable {
    @Override
    public String call() throws Exception {
        return "Result from Callable";
    }
}

// 启动线程
FutureTask futureTask = new FutureTask(new MyCallable());
Thread thread = new Thread(futureTask);
thread.start();
String result = futureTask.get(); // 获取返回值

4. 使用线程池

import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;

// 创建线程池
ExecutorService executor = Executors.newFixedThreadPool(10);

// 提交任务
executor.submit(() -> {
    System.out.println("Task executed in thread: " + Thread.currentThread().getName());
});

// 关闭线程池
executor.shutdown();

二、并发工具类

1. CountDownLatch(倒计时门闩)

import java.util.concurrent.CountDownLatch;

public class CountDownLatchExample {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(3);
        
        // 启动 3 个线程
        for (int i = 0; i  {
                System.out.println("Task " + Thread.currentThread().getName() + " completed");
                latch.countDown();
            }).start();
        }
        
        // 等待所有任务完成
        latch.await();
        System.out.println("All tasks completed");
    }
}

2. CyclicBarrier(循环屏障)

import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierExample {
    public static void main(String[] args) {
        CyclicBarrier barrier = new CyclicBarrier(3, () -> {
            System.out.println("All threads reached barrier");
        });
        
        for (int i = 0; i  {
                System.out.println("Thread " + Thread.currentThread().getName() + " working");
                try {
                    barrier.await();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

3. Semaphore(信号量)

import java.util.concurrent.Semaphore;

public class SemaphoreExample {
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(3); // 允许 3 个线程同时访问
        
        for (int i = 0; i  {
                try {
                    semaphore.acquire();
                    System.out.println("Thread " + Thread.currentThread().getName() + " accessing resource");
                    Thread.sleep(1000);
                    semaphore.release();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

4. ReentrantLock(可重入锁)

import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockExample {
    private final ReentrantLock lock = new ReentrantLock();
    private int counter = 0;
    
    public void increment() {
        lock.lock();
        try {
            counter++;
        } finally {
            lock.unlock();
        }
    }
}

三、常见坑点与解决方案

1. 线程安全问题

问题:多个线程同时修改共享变量导致数据不一致

解决方案

  • 使用 volatile 关键字保证可见性
  • 使用 synchronized 关键字保证原子性
  • 使用 ConcurrentHashMap 替代 HashMap
  • 使用原子类(AtomicInteger、AtomicLong 等)

2. 死锁问题

问题:两个或多个线程互相等待对方释放锁

解决方案

  • 避免嵌套锁
  • 使用 tryLock() 和超时机制
  • 按固定顺序获取锁

3. 内存可见性问题

问题:一个线程修改了变量,其他线程看不到修改

解决方案

  • 使用 volatile 关键字
  • 使用 synchronized 或 ReentrantLock
  • 使用 ThreadLocal 隔离线程状态

4. 线程池配置不当

问题:线程池过大导致内存溢出,过小导致性能问题

最佳实践

// CPU 密集型任务
int cpuCount = Runtime.getRuntime().availableProcessors();
ExecutorService cpuExecutor = Executors.newFixedThreadPool(cpuCount + 1);

// IO 密集型任务
int ioThreads = cpuCount * 2;
ExecutorService ioExecutor = Executors.newFixedThreadPool(ioThreads);

四、最佳实践

  1. 优先使用线程池:避免频繁创建和销毁线程
  2. 使用不可变对象:减少线程同步需求
  3. 合理设计锁粒度:避免锁竞争
  4. 使用并发容器:如 ConcurrentHashMap、BlockingQueue 等
  5. 避免在锁中执行耗时操作:提高并发性能
  6. 使用 CompletableFuture 进行异步编程:简化异步代码

五、总结

Java 并发编程是提升应用性能的关键技术。掌握线程创建方式、并发工具类和常见坑点的解决方案,可以帮助开发者写出高效、安全的并发代码。在实际开发中,应遵循最佳实践,合理使用并发工具,避免常见的并发陷阱。

#Java 并发 #多线程 #Java 开发 #性能优化 #技术教程

标签

发表评论