当前位置: 首页 > news >正文

Java多线程编程与锁机制全解析(覆盖Java到Spring)

文章内容涵盖线程创建、生命周期、线程安全问题,以及synchronized``ReentrantLock、ReadWriteLock、StampedLock、volatile和ThreadLocal等锁机制的详细讲解。通过代码示例,深入剖析Java并发工具类(如CountDownLatch、CyclicBarrier、Semaphore、Exchanger)和Executor框架的应用。文章还介绍了Spring环境下的多线程编程,包括@Async和TaskExecutor的使用,以及线程池的配置方法。结合生产者-消费者、线程安全单例等高阶模式,分享最佳实践与调试技巧。

Java多线程基础

1. 线程的创建与基本操作
Java中创建线程有以下几种方式:

• 继承Thread类
• 实现Runnable接口
• 实现Callable接口(带返回值)
• 使用Lambda表达式(Java 8+)

示例代码:创建线程

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;public class ThreadCreationExample {// 继承Threadstatic class MyThread extends Thread {@Overridepublic void run() {System.out.println("MyThread running: " + Thread.currentThread().getName());}}// 实现Runnablestatic class MyRunnable implements Runnable {@Overridepublic void run() {System.out.println("MyRunnable running: " + Thread.currentThread().getName());}}// 实现Callablestatic class MyCallable implements Callable<String> {@Overridepublic String call() {return "MyCallable result from: " + Thread.currentThread().getName();}}public static void main(String[] args) throws Exception {// 方式1:ThreadMyThread thread = new MyThread();thread.start();// 方式2:RunnableThread runnableThread = new Thread(new MyRunnable());runnableThread.start();// 方式3:Callable with FutureTaskFutureTask<String> futureTask = new FutureTask<>(new MyCallable());Thread callableThread = new Thread(futureTask);callableThread.start();System.out.println(futureTask.get()); // 获取结果// 方式4:LambdaThread lambdaThread = new Thread(() -> System.out.println("Lambda thread running: " + Thread.currentThread().getName()));lambdaThread.start();}
}

讲解:

• Thread:直接继承Thread类,重写run()方法,调用start()启动线程。
• Runnable:实现Runnable接口,适合资源共享,因为可以多个线程共享同一个Runnable实例。
• Callable:与Runnable类似,但可以返回结果或抛出异常,通常与FutureTask或线程池一起使用。
• Lambda:Java 8引入的函数式编程方式,简洁且适合快速定义任务。

2. 线程生命周期
线程有以下状态:

• 新建(New):创建后未调用start()。
• 可运行(Runnable):调用start()后,线程进入可运行状态,可能正在运行或等待CPU。
• 阻塞(Blocked):等待锁(例如synchronized)。
• 等待(Waiting):调用wait()、join()或LockSupport.park(),进入等待状态。
• 定时等待(Timed Waiting):调用sleep()、wait(timeout)等,等待指定时间。
• 终止(Terminated):线程执行完毕或异常退出。

示例代码:线程状态

public class ThreadStateExample {public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(() -> {try {Thread.sleep(1000); // 进入Timed Waiting} catch (InterruptedException e) {e.printStackTrace();}});System.out.println("New state: " + thread.getState()); // NEWthread.start();System.out.println("Runnable state: " + thread.getState()); // RUNNABLEThread.sleep(100); // 等待线程进入睡眠System.out.println("Timed Waiting state: " + thread.getState()); // TIMED_WAITINGThread.sleep(1000); // 等待线程结束System.out.println("Terminated state: " + thread.getState()); // TERMINATED}
}

3. 线程安全问题
多线程访问共享资源可能导致数据不一致或线程竞争问题。例如:

public class ThreadSafetyIssue {private static int counter = 0;public static void main(String[] args) throws InterruptedException {Runnable task = () -> {for (int i = 0; i < 1000; i++) {counter++; // 非原子操作,可能导致数据不一致}};Thread t1 = new Thread(task);Thread t2 = new Thread(task);t1.start();t2.start();t1.join();t2.join();System.out.println("Counter: " + counter); // 可能不是2000}
}

问题:
counter++不是原子操作(包含读取、修改、写入三个步骤),多线程并发执行可能导致计数丢失。

线程同步与锁机制

为了解决线程安全问题,Java提供了多种同步和锁机制。

1. synchronized关键字
synchronized可以修饰方法或代码块,确保同一时间只有一个线程访问受保护的资源。

示例代码:synchronized

public class SynchronizedExample {private static int counter = 0;public synchronized void increment() { // 同步方法counter++;}public static void main(String[] args) throws InterruptedException {SynchronizedExample example = new SynchronizedExample();Runnable task = () -> {for (int i = 0; i < 1000; i++) {example.increment();}};Thread t1 = new Thread(task);Thread t2 = new Thread(task);t1.start();t2.start();t1.join();t2.join();System.out.println("Counter: " + counter); // 总是2000}
}

讲解:

• 对象锁:synchronized方法锁住的是当前对象(this)。
• 类锁:static synchronized方法或synchronized(ClassName.class)锁住整个类。
• 代码块:synchronized(obj)可以锁定任意对象,灵活性更高。

2. ReentrantLock
ReentrantLock是java.util.concurrent.locks包中的可重入锁,提供比synchronized更灵活的功能:

• 支持公平锁/非公平锁
• 支持超时尝试锁(tryLock)
• 支持条件变量(Condition)
示例代码:ReentrantLock

import java.util.concurrent.locks.ReentrantLock;public class ReentrantLockExample {private static int counter = 0;private static final ReentrantLock lock = new ReentrantLock();public void increment() {lock.lock(); // 获取锁try {counter++;} finally {lock.unlock(); // 确保释放锁}}public static void main(String[] args) throws InterruptedException {ReentrantLockExample example = new ReentrantLockExample();Runnable task = () -> {for (int i = 0; i < 1000; i++) {example.increment();}};Thread t1 = new Thread(task);Thread t2 = new Thread(task);t1.start();t2.start();t1.join();t2.join();System.out.println("Counter: " + counter); // 总是2000}
}

讲解:

• lock():获取锁,如果锁被占用则阻塞。
• unlock():释放锁,必须在finally块中调用以避免死锁。
• tryLock():尝试获取锁,可指定超时时间,避免无限等待。
• Condition:可以创建多个条件变量,类似wait()/notify()。

3. ReadWriteLock
ReadWriteLock支持读写分离,允许多个线程同时读,但写操作互斥。

示例代码:ReadWriteLock

import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;public class ReadWriteLockExample {private int data = 0;private final ReadWriteLock lock = new ReentrantReadWriteLock();public void write(int value) {lock.writeLock().lock();try {data = value;System.out.println("Write: " + data);} finally {lock.writeLock().unlock();}}public int read() {lock.readLock().lock();try {System.out.println("Read: " + data);return data;} finally {lock.readLock().unlock();}}public static void main(String[] args) {ReadWriteLockExample example = new ReadWriteLockExample();new Thread(() -> {example.write(100);}).start();for (int i = 0; i < 3; i++) {new Thread(() -> {example.read();}).start();}}
}

讲解:

• readLock():允许多个线程同时读取,提高并发性能。
• writeLock():写操作互斥,确保数据一致性。
• 适合读多写少的场景,如缓存系统。

4. StampedLock
StampedLock是Java 8引入的锁,性能优于ReadWriteLock,支持乐观读。

示例代码:StampedLock

import java.util.concurrent.locks.StampedLock;public class StampedLockExample {private int data = 0;private final StampedLock lock = new StampedLock();public void write(int value) {long stamp = lock.writeLock();try {data = value;System.out.println("Write: " + data);} finally {lock.unlockWrite(stamp);}}public int readOptimistic() {long stamp = lock.tryOptimisticRead();int result = data;if (!lock.validate(stamp)) { // 检查数据是否被修改stamp = lock.readLock(); // 降级为悲观读try {result = data;System.out.println("Pessimistic Read: " + result);} finally {lock.unlockRead(stamp);}} else {System.out.println("Optimistic Read: " + result);}return result;}public static void main(String[] args) {StampedLockExample example = new StampedLockExample();new Thread(() -> {example.write(100);}).start();for (int i = 0; i < 3; i++) {new Thread(() -> {example.readOptimistic();}).start();}}
}

讲解:

• 乐观读:tryOptimisticRead()假设数据未被修改,读取后通过validate()检查。
• 悲观读:如果验证失败,升级为readLock()。
• 不支持重入:不像ReentrantLock,需要注意避免死锁。

5. volatile关键字
volatile确保变量的可见性和禁止指令重排序,但不保证原子性。

示例代码:volatile

public class VolatileExample {private volatile boolean running = true;public void stop() {running = false;}public void run() {while (running) {System.out.println("Running...");try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("Stopped");}public static void main(String[] args) throws InterruptedException {VolatileExample example = new VolatileExample();Thread t = new Thread(example::run);t.start();Thread.sleep(500);example.stop();}
}

讲解:

• 可见性:volatile确保一个线程的修改对其他线程立即可见。
• 适用场景:适合简单的状态标志(如running),但不适合复合操作(如counter++)。

6. ThreadLocal
ThreadLocal为每个线程提供独立的变量副本,避免共享资源竞争。

示例代码:ThreadLocal

public class ThreadLocalExample {private static final ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 0);public void increment() {threadLocal.set(threadLocal.get() + 1);System.out.println(Thread.currentThread().getName() + ": " + threadLocal.get());}public static void main(String[] args) {ThreadLocalExample example = new ThreadLocalExample();for (int i = 0; i < 3; i++) {new Thread(example::increment).start();}}
}

讲解:

• 每个线程有自己的threadLocal值,互不干扰。
• 注意:使用后需调用remove()以避免内存泄漏。

Java并发工具类

1. CountDownLatch
CountDownLatch用于等待一组线程完成。

示例代码:CountDownLatch

import java.util.concurrent.CountDownLatch;public class CountDownLatchExample {public static void main(String[] args) throws InterruptedException {CountDownLatch latch = new CountDownLatch(3);Runnable task = () -> {System.out.println(Thread.currentThread().getName() + " completed");latch.countDown();};for (int i = 0; i < 3; i++) {new Thread(task).start();}latch.await(); // 主线程等待System.out.println("All tasks completed");}
}

讲解:

• countDown():计数减一。
• await():等待计数归零。
• 适合主线程等待子线程完成初始化等场景。

2. CyclicBarrier
CyclicBarrier用于让一组线程到达某个屏障点后同时继续。

示例代码: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"));Runnable task = () -> {System.out.println(Thread.currentThread().getName() + " preparing");try {barrier.await();System.out.println(Thread.currentThread().getName() + " continued");} catch (Exception e) {e.printStackTrace();}};for (int i = 0; i < 3; i++) {new Thread(task).start();}}
}

讲解:

• await():线程到达屏障点后等待其他线程。
• 可重用,适合循环任务。

3. Semaphore
Semaphore控制并发访问资源的线程数。

示例代码:Semaphore

import java.util.concurrent.Semaphore;public class SemaphoreExample {private static final Semaphore semaphore = new Semaphore(2); // 允许2个线程并发public static void main(String[] args) {Runnable task = () -> {try {semaphore.acquire();System.out.println(Thread.currentThread().getName() + " acquired permit");Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();} finally {semaphore.release();System.out.println(Thread.currentThread().getName() + " released permit");}};for (int i = 0; i < 5; i++) {new Thread(task).start();}}
}

讲解:

• acquire():获取许可,若无可用许可则阻塞。
• release():释放许可。
• 适合限流场景,如数据库连接池。

4. Exchanger
Exchanger用于两个线程交换数据。

示例代码:Exchanger

import java.util.concurrent.Exchanger;public class ExchangerExample {public static void main(String[] args) {Exchanger<String> exchanger = new Exchanger<>();new Thread(() -> {try {String data = exchanger.exchange("Data from Thread 1");System.out.println("Thread 1 received: " + data);} catch (InterruptedException e) {e.printStackTrace();}}).start();new Thread(() -> {try {String data = exchanger.exchange("Data from Thread 2");System.out.println("Thread 2 received: " + data);} catch (InterruptedException e) {e.printStackTrace();}}).start();}
}

讲解:

• exchange():等待另一个线程到达交换点,交换数据。
• 适合双线程协作场景。

Executor框架与线程池

1. ExecutorService
ExecutorService是Java并发框架的核心,简化线程管理。

示例代码:ExecutorService

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ExecutorServiceExample {public static void main(String[] args) {ExecutorService executor = Executors.newFixedThreadPool(2);for (int i = 0; i < 5; i++) {final int taskId = i;executor.submit(() -> {System.out.println("Task " + taskId + " running in " + Thread.currentThread().getName());try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}});}executor.shutdown(); // 关闭线程池}
}

讲解:

• newFixedThreadPool:固定大小线程池。
• newCachedThreadPool:动态调整线程数,适合短任务。
• newSingleThreadExecutor:单线程执行,顺序处理任务。
• submit():提交任务,支持Runnable或Callable。
• shutdown():优雅关闭线程池。

2. ThreadPoolExecutor
ThreadPoolExecutor提供更细粒度的控制。

示例代码:ThreadPoolExecutor

import java.util.concurrent.*;public class ThreadPoolExecutorExample {public static void main(String[] args) {ThreadPoolExecutor executor = new ThreadPoolExecutor(2, // 核心线程数4, // 最大线程数60, TimeUnit.SECONDS, // 空闲线程存活时间new ArrayBlockingQueue<>(3), // 任务队列Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy() // 拒绝策略);for (int i = 0; i < 7; i++) {final int taskId = i;executor.execute(() -> {System.out.println("Task " + taskId + " running in " + Thread.currentThread().getName());try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}});}executor.shutdown();}
}

讲解:

• 核心线程数:始终保持的线程数。
• 最大线程数:当队列满时,允许创建的最大线程数。
• 拒绝策略:队列满且线程数达到最大时的处理方式(如抛异常、丢弃任务等)。

3. Fork/Join框架
Fork/Join框架用于分治任务,适合递归分解的大任务。

示例代码:Fork/Join

import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;public class ForkJoinExample {static class SumTask extends RecursiveTask<Long> {private final int[] array;private final int start, end;private static final int THRESHOLD = 10;public SumTask(int[] array, int start, int end) {this.array = array;this.start = start;this.end = end;}@Overrideprotected Long compute() {if (end - start <= THRESHOLD) {long sum = 0;for (int i = start; i < end; i++) {sum += array[i];}return sum;} else {int mid = start + (end - start) / 2;SumTask leftTask = new SumTask(array, start, mid);SumTask rightTask = new SumTask(array, mid, end);leftTask.fork(); // 异步执行左子任务return rightTask.compute() + leftTask.join(); // 计算右子任务并合并}}}public static void main(String[] args) {int[] array = new int[100];for (int i = 0; i < array.length; i++) {array[i] = i + 1;}ForkJoinPool pool = ForkJoinPool.commonPool();long result = pool.invoke(new SumTask(array, 0, array.length));System.out.println("Sum: " + result);}
}

讲解:

• fork():将任务分解为子任务并异步执行。
• join():等待子任务完成并获取结果。
• 适合CPU密集型任务,如数组求和、排序等。

Spring环境下的多线程

1. Spring的@Async注解
Spring通过@Async实现异步任务,需启用异步支持。

示例代码:@Async

import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;@Configuration
@EnableAsync
public class AsyncConfig {
}@Service
class AsyncService {@Asyncpublic void asyncTask() {System.out.println("Async task running in " + Thread.currentThread().getName());try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}
}@SpringBootApplication
public class Application {public static void main(String[] args) {ApplicationContext context = SpringApplication.run(Application.class, args);AsyncService service = context.getBean(AsyncService.class);service.asyncTask();System.out.println("Main thread: " + Thread.currentThread().getName());}
}

讲解:

• @EnableAsync:启用异步支持。
• @Async:标记方法为异步执行,默认使用SimpleAsyncTaskExecutor。
• 注意:异步方法不能在同一类中调用,否则不会生效。

2. TaskExecutor
Spring的TaskExecutor是线程池的抽象,常用ThreadPoolTaskExecutor。

示例代码:TaskExecutor

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;@Configuration
public class TaskExecutorConfig {@Beanpublic ThreadPoolTaskExecutor taskExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(2);executor.setMaxPoolSize(4);executor.setQueueCapacity(10);executor.setThreadNamePrefix("MyThread-");executor.initialize();return executor;}
}@Service
class AsyncService {@Async("taskExecutor")public void asyncTask() {System.out.println("Async task running in " + Thread.currentThread().getName());}
}

讲解:

• ThreadPoolTaskExecutor:Spring对ThreadPoolExecutor的封装。
• @Async(“taskExecutor”):指定特定的线程池。

高阶并发模式与最佳实践

1. 生产者-消费者模式
使用BlockingQueue实现生产者-消费者模式。

示例代码:生产者-消费者

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;public class ProducerConsumerExample {public static void main(String[] args) {BlockingQueue<Integer> queue = new LinkedBlockingQueue<>(10);// 生产者Runnable producer = () -> {try {for (int i = 0; i < 20; i++) {queue.put(i);System.out.println("Produced: " + i);Thread.sleep(100);}} catch (InterruptedException e) {e.printStackTrace();}};// 消费者Runnable consumer = () -> {try {while (true) {Integer value = queue.take();System.out.println("Consumed: " + value);Thread.sleep(200);}} catch (InterruptedException e) {e.printStackTrace();}};new Thread(producer).start();new Thread(consumer).start();}
}

讲解:

• put():阻塞放入元素。
• take():阻塞获取元素。
• 适合异步任务处理,如消息队列。

2. 线程安全的单例模式
使用双检锁(DCL)或静态内部类实现线程安全的单例。

示例代码:双检锁单例

public class Singleton {private static volatile Singleton instance;private Singleton() {}public static Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton();}}}return instance;}
}

讲解:

• volatile:防止指令重排序,确保初始化完成。
• 双检锁:减少同步开销。

3. 并发集合
Java提供线程安全的集合,如:

• ConcurrentHashMap:线程安全的哈希表,支持高并发读写。
• CopyOnWriteArrayList:适合读多写少的场景。
• BlockingQueue:如LinkedBlockingQueue、ArrayBlockingQueue。
示例代码:ConcurrentHashMap

import java.util.concurrent.ConcurrentHashMap;public class ConcurrentHashMapExample {public static void main(String[] args) {ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();Runnable task = () -> {for (int i = 0; i < 1000; i++) {map.compute("key", (k, v) -> v == null ? 1 : v + 1);}};Thread t1 = new Thread(task);Thread t2 = new Thread(task);t1.start();t2.start();try {t1.join();t2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Value: " + map.get("key")); // 总是2000}
}

常见问题与调试技巧

1. 死锁
死锁是多个线程互相等待对方释放资源。

示例代码:死锁

public class DeadlockExample {public static void main(String[] args) {Object lock1 = new Object();Object lock2 = new Object();new Thread(() -> {synchronized (lock1) {System.out.println("Thread 1: Holdingទlock1");try {Thread.sleep(100);} catch (InterruptedException e) {}synchronized (lock2) {System.out.println("Thread 1: Holding lock1, waiting for lock2");}}).start();new Thread(() -> {synchronized (lock2) {System.out.println("Thread 2: Holding lock2, waiting for lock1");try {Thread.sleep(100);} catch (InterruptedException e) {}synchronized (lock1) {System.out.println("Thread 2: Got lock1");}}).start();}}
}

解决方法:

• 避免嵌套锁。
• 使用ReentrantLock的超时锁。
• 确保锁的获取顺序一致。

2. 调试技巧
• Thread Dump:使用jstack或kill -3获取线程堆栈,分析线程状态。
• 日志:添加详细日志,记录线程操作。
• 工具:使用JVisualVM、JProfiler等工具监控线程和锁。
结语
• 基础:通过Thread、Runnable、Callable创建线程,理解生命周期和安全问题。
• 锁机制:synchronized、 ReentrantLock、 ReadWriteLock、 StampedLock、 volatile、 ThreadLocal各有适用场景。
• 并发工具:CountDownLatch、 CyclicBarrier、 Semaphore、 Exchanger解决特定协作问题。
• 线程池:ExecutorService和ThreadPoolExecutor管理线程,Fork/Join适合分治任务。
• Spring:@Async和TaskExecutor简化异步任务管理。
• 高阶:生产者-消费者、线程安全单例和并发集合是常见模式。
• 注意事项:避免死锁、内存泄漏,善用调试工具。

http://www.dtcms.com/a/345040.html

相关文章:

  • 从0到1打造一台机器人走起来
  • 技术解读|MatrixOne高效 CDC:基于快照的分布式数据库优化方案
  • AI如何赋能财务分析:1份财务报表录入从数小时到5分钟
  • 声网SDK更新,多场景抗弱网稳定性大幅增强
  • 制造企业用档案宝,档案清晰可查
  • ArrayList线程不安全问题及解决方案详解
  • AI:业务驱动与技术赋能:企业智能化应用的双向进化深度指南
  • 红酒数据集预处理实战:缺失值处理的 5 种打开方式,从入门到进阶一步到位
  • vue-admin-template权限管理
  • 信创认证是什么?怎么报考?
  • 特级资质信息化迎检核心流程经验分享
  • Pod控制器详解
  • STM32之ADC详解
  • [系统架构设计师]大数据架构设计理论与实践(十九)
  • ​维基框架 (Wiki Framework) 1.1.0 版本发布​ 提供多模型AI辅助开发
  • TNS(ORACLE)协议分析
  • [硬件电路-162]:PID参数受哪些因素影响?
  • 【Redis】缓存和分布式锁
  • MySQL - 视图,事务和索引
  • AAA 服务器与 RADIUS 协议笔记
  • C语言初学笔记【联合与枚举】
  • Unreal Engine USceneComponent
  • 如何实现二维CAD与3D建模工程图关联一体化出图 | 中望3D 2026新亮点
  • android sdk 虚拟机是否可以通过命令行打开?
  • 数字逻辑与数字系统设计之电梯控制器设计
  • 防爆连接器在防爆箱上的作用
  • shell脚本第二阶段-----选择结构
  • Unreal Engine IWYU Include What You Use
  • DLT645仪表通信,串口助手调试读写地址
  • 【C#】观察者模式 + UI 线程调度、委托讲解