前言
本篇博客主要汇总Java高并发相关的知识点,通过阅读博客,希望能对相关知识点进行串联起来,形成体系O(∩_∩)O哈哈~
Java多线程基础知识总结
- 线程的生命周期:新建状态->就绪状态->运行状态->阻塞状态->终止状态
- 线程的创建方式:
- 继承Thread类,重写run()方法
- 实现Runnable接口,实现run()方法,传递给Thread类的构造方法
- 实现Callable接口,重写call()方法,传递给FutureTask类,再传递给Thread类
- 线程的启动:
- 调用start()方法,让线程进入就绪状态,等待JVM调用,进入运行状态
- 不能直接调用run()方法,那样就变成了普通方法调用,没有开启新的线程
- 线程的中断:
- 调用线程的interrupt()方法,设置中断标识位
- 被中断的线程可以通过isInterrupted()判断中断标识位是否设置,进行相应处理
- 静态方法Thread.interrupted()可以清除当前线程的中断标识位
- 线程的挂起与恢复:
- 调用线程的suspend()方法,将线程挂起,进入阻塞状态
- 调用线程的resume()方法,将线程恢复,重新进入就绪状态
- 线程的优先级:
- 可以通过setPriority()设置线程的优先级,优先级高的线程比较易获取CPU执行
- 优先级范围是1-10,默认是5
- 不能保证高优先级的线程一定先执行
- 线程间通信:
- wait():让当前线程等待,直到其他线程调用notify()或notifyAll()方法唤醒当前线程
- notify():唤醒等待在对象的监视器上的单个线程
- notifyAll():唤醒等待在对象的监视器上的所有线程
Java线程池详解
- 为什么要用线程池?
- 重用线程,减少对象创建和销毁的开销,提高响应速度
- 有限制的线程数量,防止过度消耗系统资源
- 方便线程管理,统一调度
- 线程池的结构
- 任务队列:用于存放等待执行的任务
- 工作线程:执行任务的线程
- 线程池管理器:管理线程的增减和调度等
- Java中的线程池框架:Executor框架
- Executor:定义了执行任务的规范
- ExecutorService:继承Executor,定义了管理执行器生命周期的服务
- ThreadPoolExecutor:实现ExecutorService,表示线程池
- Executors:工厂类,用于创建不同配置的线程池
- ThreadPoolExecutor的参数说明
- corePoolSize:核心池大小,默认情况下创建的线程数
- maximumPoolSize:最大线程数,超过这个数字就会用SynchronousQueue来暂存任务
- keepAliveTime:非核心线程闲置时间,超时就回收
- unit:keepAliveTime的单位
- workQueue:任务队列,缓存执行任务的队列
- threadFactory:线程工厂,用于创建线程
- handler:拒绝策略,当队列和线程池都满了,采取的策略
- ThreadPoolExecutor的执行流程
- 首先将任务加入任务队列workQueue,如果成功,则等待被工作线程执行
- 如果工作线程少于corePoolSize,则创建新线程执行任务
- 如果工作线程等于或多于corePoolSize,将任务加入workQueue
- 如果workQueue已满,但工作线程少于maximumPoolSize,则创建新线程执行任务
- 如果workQueue已满和工作线程等于maximumPoolSize,采取handler的拒绝策略处理任务
- 空闲的工作线程等待keepAliveTime后会被回收
ThreadLocal简介
- ThreadLocal的作用是提供线程本地变量,每个线程都可以有自己单独的变量副本,可以解决多线程程序中变量传递的问题。
- ThreadLocal内部的实现原理是维护一个Map,key是线程对象,value是线程本地变量副本。不同的线程访问ThreadLocal变量时,会访问不同的副本,相互不会产生影响。
- ThreadLocal常见的使用场景:
- 数据库连接:每个线程维护自己的数据库连接,相互隔离
- 事务管理:每个线程管理自己的事务,不会对其他线程产生影响
- Servlet:每个线程维护自己的HttpServletRequest和HttpServletResponse对象
- 随机数:每个线程有自己独立的随机数生成器
- ThreadLocal的常用API:
- ThreadLocal():创建ThreadLocal对象
- set(T value):设置当前线程的线程本地变量的值
- get():获取当前线程的线程本地变量的值
- remove():移除当前线程的线程本地变量的值
- initialValue():返回当前线程本地变量的初始值
- ThreadLocal内存泄漏问题:
ThreadLocalMap是ThreadLocal维护的一个静态内部类,用于存储每个线程的变量副本。
如果一个ThreadLocal没有被回收,它对ThreadLocalMap的引用就不会消失,导致线程无法被回收,造成内存泄漏。
解决方法:
手动调用remove()方法或重写ThreadLocal的finalize()方法。
ThreadLocal为解决多线程变量传递问题提供了一种轻量级的机制,但也容易出现内存泄漏问题,需要在使用时充分考虑。Java守护线程
Java守护线程(Daemon Thread)是运行在后台的一种特殊线程。
它的主要作用是为其他线程的运行提供便利服务,守护线程并不属于程序中不可或缺的线程,如果所有的非守护线程结束了运行,程序也就终止,同时守护线程也会被强制结束。
主要特征: - 守护线程是为用户线程提供服务的线程,它们没有执行用户的主要任务;
- 当程序中所有的用户线程结束时,程序也就终止运行,同时守护线程也会被强制结束;
- 在程序启动时,Java Virtual Machine(JVM)会将所有的守护线程设置为守护线程。如果你的程序没有设置daemon线程,那默认该程序没有守护线程;
- 可以通过调用setDeamon()方法设置线程为守护线程或用户线程。但是,这个调用必须在线程启动前进行,一旦线程启动后,不能改变其线程身份;
- 主线程默认是用户线程。
使用场景: - 垃圾回收线程就是一个典型的守护线程示例;
- 定时分析内存使用率的线程;
- 定期执行日志文件与数据库同步的线程;
- 定期重启应用服务的线程,高可用性就需要用到;
示例代码:public class DaemonThreadExample { public static void main(String[] args) { Thread thread = new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " end!"); } }); thread.setDaemon(true); // 将线程设置为守护线程 thread.start(); System.out.println(Thread.currentThread().getName() + " end!"); } }
结果:
main end!
说明守护线程thread被随着主线程main的结束而结束。Java多线程之Executor框架
Executor框架是Java提供的一种线程池管理框架。它提供了一种标准的任务执行和调度机制,可以大大简化在多线程情况下执行任务的代码。
主要接口和类:- Executor:用于执行任务的简单接口。定义了execute(Runnable)方法,用于执行任务。
- ExecutorService:继承自Executor,添加了线程池的控制和管理方法。添加了submit、invokeAll、invokeAny以及shutDown等方法。常见的实现有ThreadPoolExecutor和ScheduledThreadPoolExecutor。
- ScheduledExecutorService:继承自ExecutorService,添加了定时任务和周期任务的执行方法。如schedule、scheduleAtFixedRate和scheduleWithFixedDelay。
- ThreadPoolExecutor:线程池的主要实现类。构造方法比较复杂,一般使用Executors来创建。
- Executors:线程池工厂类,用于创建不同类型的线程池。常用的有:
- newCachedThreadPool():创建一个可缓存的线程池。线程池的线程数可以随需要增加到最大值,然后回收空闲线程,存活时间指定。
- newFixedThreadPool(n):创建一个固定大小的线程池。线程数指定为n,允许的最大值也是n。
- newSingleThreadExecutor():创建一个只有一个线程的线程池。相当于newFixedThreadPool(1)。
- newScheduledThreadPool(n):创建一个大小为n的线程池,处理定时任务和周期任务。
示例代码:
public class ExecutorTest {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " running!");
}
});
}
executorService.shutdown();
}
}
结果:
pool-1-thread-1 running!
pool-1-thread-3 running!
pool-1-thread-2 running!
pool-1-thread-2 running!
...
可以看到线程池会重复使用固定数量的线程来执行任务,并且按顺序从线程池获取线程。
同步工具类CountDownLatch
CountDownLatch 是 Java 提供的一个同步工具类,用于协调多个线程之间的同步。
主要作用是:使一个线程在等待其他线程完成各自工作之后再继续执行。
构造方法:
- CountDownLatch(int count):创建一个CountDownLatch对象,初始化计数器count。
主要方法: - countDown():当前计数器的值减1。
- await():调用该方法的线程会被阻塞,直到计数器的值为0才返回继续执行。
- getCount():返回当前计数器的值。
示例代码:public class CountDownLatchTest { public static void main(String[] args) throws InterruptedException { CountDownLatch countDownLatch = new CountDownLatch(3); new Thread(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + " running!"); countDownLatch.countDown(); } }).start(); new Thread(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + " running!"); countDownLatch.countDown(); } }).start(); new Thread(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + " running!"); countDownLatch.countDown(); } }).start(); countDownLatch.await(); System.out.println(Thread.currentThread().getName() + " continue running!"); } }
运行结果:
Thread-0 running!
Thread-1 running!
Thread-2 running!
main continue running!
可以看到,main线程调用await()方法后被阻塞,直到其他3个线程都运行完成调用countDown()后,才继续执行。
这就是 CountDownLatch 的基本作用,用于线程之间的同步和执行顺序控制。通过socket、多线程、动态代理、反射 实现RPC远程方法调用
RPC(Remote Procedure Call)远程过程调用,是一种进程间通信方式,允许一个进程调用另一个进程中的方法,让调用进程获得另一个网络地址空间中运行的方法的返回值。
实现RPC的关键步骤:- 在服务端开启ServerSocket,监听客户端连接
- 客户端通过Socket连接服务端,发送调用的方法名称和参数
- 服务端接收客户端发送的方法名称和参数
- 通过反射调用方法,获得返回值
- 将返回值发送给客户端
- 客户端接收返回值
具体实现代码如下:
服务端:public class RpcServer { public static void main(String[] args) throws Exception{ ServerSocket serverSocket = new ServerSocket(8888); while (true) { Socket client = serverSocket.accept(); new Thread(new Runnable() { @Override public void run() { try { ObjectInputStream input = new ObjectInputStream(client.getInputStream()); String methodName = input.readUTF(); Class<?>[] parameterTypes = (Class<?>[]) input.readObject(); Object[] arguments = (Object[]) input.readObject(); Object result = methodInvoke(methodName, parameterTypes, arguments); ObjectOutputStream output = new ObjectOutputStream(client.getOutputStream()); output.writeObject(result); } catch (Exception e) { e.printStackTrace(); } } }).start(); } } private static Object methodInvoke(String methodName, Class<?>[] parameterTypes, Object[] arguments) throws Exception { Method method = Class.forName("java.lang.Math").getMethod(methodName, parameterTypes); return method.invoke(null, arguments); } }
客户端:
public class RpcClient { public static void main(String[] args) throws Exception{ Socket socket = new Socket("127.0.0.1", 8888); ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream()); output.writeUTF("pow"); output.writeObject(new Class[] {double.class, double.class}); output.writeObject(new Object[] {2, 3}); ObjectInputStream input = new ObjectInputStream(socket.getInputStream()); Object result = input.readObject(); System.out.println(result); } }
运行结果:
8.0
可以看到,客户端调用Math.pow(2, 3)方法,服务端通过反射执行该方法,并将返回值发送给客户端,实现了RPC调用。同步容器与并发容器
同步容器与并发容器是Java提供的两种容器类,主要区别在于:
同步容器:
- 内部使用同步锁(synchronized)来控制线程对容器的访问,保证线程安全;
- 但同一时间只允许一个线程访问容器,效率较低。
并发容器: - 使用更高级的锁(如CAS算法)和数据结构来保证线程安全,可以允许多个线程并发访问容器;
- 效率比同步容器高,适用于高并发场景。
主要的同步容器有: - Hashtable:同步的HashMap;
- Vector:同步的ArrayList;
- Stack:同步的Stack。
主要的并发容器有: - ConcurrentHashMap:高效的HashMap;
- CopyOnWriteArrayList:线程安全的ArrayList;
- BlockingQueue:阻塞队列,如ArrayBlockingQueue和LinkedBlockingQueue。
示例代码:
同步容器(Hashtable):Hashtable<String, Integer> hashtable = new Hashtable<>(); hashtable.put("key1", 1); hashtable.put("key2", 2);
并发容器(ConcurrentHashMap):
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>(); map.put("key1", 1); map.put("key2", 2);
可以看出,在声明和使用上,并发容器与同步容器非常相似,但是在内部实现上使用了更高级的锁和算法来实现线程安全,比同步容器效率更高。
所以,在选择容器类时,如果高并发是重要考量因素,应该优先选择并发容器;如果对并发要求不高,同步容器也可以满足需求。Java高并发编程实战-锁
Java高并发编程中,锁是控制线程同步访问共享资源的主要手段。主要的锁有:
- 互斥锁(mutex):同一时间只允许一个线程访问共享资源,如synchronized关键字实现的锁。
- 读写锁(read-write lock):同一时间允许多个线程读共享资源,但只允许一个线程写共享资源。如ReentrantReadWriteLock实现。
- 信号量(semaphore):通过维持一个计数器和两个队列(可选的)来控制资源的访问,并发程度可以设置。常用来实现资源池。
- 栅栏(barrier):多个线程必须等待其他线程到达某个执行点后再继续执行,常用来实现多阶段计算。如CyclicBarrier实现。
示例代码:
互斥锁(synchronized):public synchronized void method() { // ... }
读写锁(ReentrantReadWriteLock):
ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); ReentrantReadWriteLock.ReadLock readLock = lock.readLock(); ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock(); readLock.lock(); // 上读锁 // ... 读操作 readLock.unlock(); writeLock.lock(); // 上写锁 // ... 写操作 writeLock.unlock();
信号量(Semaphore):
Semaphore semaphore = new Semaphore(5); // 最多5个并发线程 semaphore.acquire(); // 获取一个许可 // ... 执行操作 semaphore.release(); // 释放一个许可
栅栏(CyclicBarrier):
CyclicBarrier barrier = new CyclicBarrier(3); // 三个线程到达栅栏 new Thread(() -> { // 等待其他线程 barrier.await(); // 同时继续执行 }).start(); new Thread(() -> { // 等待其他线程 barrier.await(); // 同时继续执行 }).start(); barrier.await(); // 继续执行主线程
Java高并发编程实战-原子性、可见性、有序性
在Java高并发编程中,需要关注三个重要特征:
- 原子性:一个操作或者多次操作,要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。简单来说,就是要么一块执行,要么一块不执行。
- 可见性:当一个线程修改了共享变量的值,其他线程能够立即看到修改的值。
- 有序性:代码在执行的顺序与编写的顺序一致。
Java提供的主要手段来保证这三个特征分别是: - 原子性:使用CAS(Compare And Swap)算法实现的原子操作类AtomicInteger、AtomicLong等;lock锁;synchronized等。
- 可见性: volatile关键字;synchronized及其同步方法和代码块;final关键字等。
- 有序性:happens-before规则;volatile关键字;synchronized;final关键字等。
示例代码:
原子性(AtomicInteger):AtomicInteger count = new AtomicInteger(0); count.incrementAndGet(); // 自增并返回,原子操作
可见性(volatile):
public volatile int count = 0; // 线程1对count++,线程2能立即见到修改的值
有序性(synchronized):
synchronized(obj) { x = 1; // 线程执行到此,读取x的值 y = 2; // 读取y的值,由有序性保证一定在x = 1后执行 }
这三个特征在并发编程中至关重要,synchronized、volatile和CAS等手段的运用能够保证这三个特征,从而实现正确的Java高并发程序。
Java高并发编程实战-java内存模型与Java对象结构
Java内存模型定义了线程与主内存之间的抽象关系,它规定了线程访问主内存的方式,以实现Java程序在多线程环境下的正确执行。
Java对象在内存中的布局可以分为三块: - 对象头(Header):包含对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID等。
- 实例数据(Instance Data):保存对象的字段值,包括父类的字段值。
- 对象数组的长度(Array Length):如果对象是一个数组,该部分记录数组的长度。
Java内存模型规定: - 线程本地内存与主内存之间是弱一致的,线程可以把主内存中的数据放入本地内存作为副本;本地内存比主内存的访问速度快,可以提高程序效率。
- 线程对主内存与本地内存的修改必须通过同步机制来保证一致性,如lock锁、volatile变量、final变量等。
- 线程无法直接读写主内存,只能通过同步机制来间接地对主内存进行操作。每个线程都有一个本地内存,线程的读写操作基本上都是对本地内存的读写。
- 主内存包含可变和不可变变量两种数据,不可变变量可以多个线程同时读,而可变变量需要使用同步机制来保证一致。
- 对象结构包含头信息、实例数据和数组长度等部分,其中部分数据用于同步和锁定信息。
Java内存模型 bemroyjcdistryqjavamsL这些规则来定义程序在并发执行时的正确语义,保证线程之间的可见性与有序性。理解这些规则对并发编程至关重要。Java高并发编程实战-synchronized与Lock底层原理
synchronized与Lock都是Java提供的实现互斥锁的重要手段,但底层实现原理不同:
synchronized:
- 基于JVM的monitor对象实现,每个对象都有一个monitor与之关联。
- 当一个线程访问同步代码块(或方法)时,会自动获取与之关联的对象的monitor,其他试图访问该代码块的线程会被阻塞。
- 释放monitor的时机是:同步代码块执行结束;synchronized方法返回;发生异常。
- 实现可重入锁,同一个线程可以重复获取同一把锁。
Lock: - 是java.util.concurrent.locks包下的接口,实现类有ReentrantLock、ReentrantReadWriteLock等。
- 基于AQS(AbstractQueuedSynchronizer)实现,可以实现公平锁、非公平锁和可重入锁等。
- 获取和释放锁需要通过lock()和unlock()方法手动控制。
- 可以实现可轮询锁、读写锁和条件变量等高级功能。
相比而言: - synchronized是JVM实现的轻量级锁,符合大多数场景需要,开销较小;Lock是Java实现的重量级锁,功能更强大,适用于更复杂的场景。
- synchronized无法判断是否获取了锁,Lock可以判断是否获取到了锁。
- synchronized会自动释放锁,Lock需要手动释放锁unlock()。
- synchronized可重入锁,不可以中断等待锁的线程;Lock也是可重入锁,可以中断等待锁的线程。
示例代码:
synchronized:public synchronized void method() { // ... }
Lock:
Lock lock = new ReentrantLock(); lock.lock(); try { // ... } finally { lock.unlock(); }
Java高并发编程实战-异步注解@Async自定义线程池
在Java高并发编程中,使用@Async注解可以很方便地实现异步方法调用。
@Async会使用DefaultManagedExecutorService作为线程池执行异步方法。
如果想自定义线程池,可以按如下步骤实现:- 实现Executor接口,自定义线程池类,如以下MyExecutor:
public class MyExecutor implements Executor { @Override public void execute(Runnable command) { // 自定义线程池逻辑 } }
- 实现AsyncConfigurer接口,并覆盖getAsyncExecutor()方法,返回自定义的线程池实例:
public class MyAsyncConfigurer implements AsyncConfigurer { @Override public Executor getAsyncExecutor() { return new MyExecutor(); } }
- 在SpringBoot启动类上添加@EnableAsync注解启用异步方法调用功能,并指定AsyncConfigurer Bean:
@SpringBootApplication @EnableAsync(proxyTargetClass = true) public class Application implements AsyncConfigurer { @Bean public AsyncConfigurer asyncConfigurer() { return new MyAsyncConfigurer(); } }
- 在需要异步执行的方法上添加@Async注解:
@Async public void doSomething() { // ... }
- 调用异步方法,Spring会使用AsyncConfigurer中指定的Executor(线程池)来执行:
public void call() { doSomething(); }
以上步骤实现自定义Executor(线程池)用于@Async异步方法调用。@Async会更优雅和方便地实现异步功能,避免手动创建和管理线程池。
- 实现Executor接口,自定义线程池类,如以下MyExecutor:
Java高并发编程实战-通过AQS源码分析lock()锁机制
AQS(AbstractQueuedSynchronizer)是Java并发包下实现锁和同步器的基础框架,许多并发类都是基于AQS扩展实现的,如ReentrantLock、Semaphore、CountDownLatch等。
AQS使用一个volatile修饰的int类型的state来维护同步状态,通过CAS操作修改state来获取锁。当state的获取失败时,当前线程会被封装成一个Node加入到队列中,直到获取锁成功。
AQS的lock()方法实现了获取锁的主要逻辑:
public final void lock() {
if (tryAcquire(1)) {
setExclusiveOwnerThread(Thread.currentThread());
return;
}
// 获取锁失败,加入队列
addWaiter(Node.EXCLUSIVE);
for (;;) {
Node node = getNextNode();
node.predecessor.next = node; // 设置node的前驱节点
// 再次尝试获取锁
if (tryAcquire(1)) {
setExclusiveOwnerThread(Thread.currentThread());
node.predecessor = null; // 前驱节点设置为null
return;
}
if (shouldParkAfterFailedAcquire(node) && context.park()) {
// 阻塞当前线程
}
}
}
主要步骤:
-
尝试直接获取锁,通过tryAcquire()方法,成功则设置独占锁持有线程并返回。
-
若失败,则构造一个Node节点加入AQS的队列。
-
在循环中不断尝试获取锁,成功则设置持有线程,返回。
-
若失败,判断是否需要park(阻塞),是则park,等待unpark操作。
-
被unpark后继续循环,重新尝试获取锁。
这就是AQS框架实现lock()获取锁的主要流程,理解AQS的原理对学习Java高并发至关重要。Java高并发编程实战-ConcurrentHashMap详解
ConcurrentHashMap是Java并发包下的哈希表,适用于高并发的场景。它通过分段锁和CAS算法来实现线程安全,比HashTable的效率高很多。
主要特征: -
基于哈希表实现,支持快速查找和修改。
-
使用分段锁和CAS来保证线程安全,比HashTable高效。
-
允许key和value都可以为null。
-
支持计算容量和扩容,扩容时重新哈希和重组元素。
主要方法:- put(K key, V value):添加键值对,如果键存在则替换值。
- get(Object key):根据键获取值。
- size():获取键值对数量。
- isEmpty():判断是否为空。
- containsKey(Object key):判断是否包含键。
- remove(Object key):根据键删除键值对。
迭代器: - 快速失败(fail-fast)的迭代器,遍历过程中发生修改,会快速失败,抛出ConcurrentModificationException。
- KeySet、Values、EntrySet等视图的迭代器,用来遍历键、值、键值对。
ConcurrentHashMap在JDK1.8之前使用分段锁实现,锁的粒度较大,并发度不高;JDK1.8使用CAS和Synchronized来实现,大大提高了并发度。
示例代码:ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>(); map.put("key1", 1); map.put("key2", 2); Integer value = map.get("key1"); Set<String> keys = map.keySet(); for (String key : keys) { // ... }
ConcurrentHashMap是Java高并发编程中的重要数据结构,掌握其原理和用法是必须的。
总结
-
进程与线程:进程是资源分配的最小单位,线程是CPU调度的最小单位。Java程序运行在JVM进程中,可以启动多个线程。
-
线程安全:多个线程访问同一资源时,需要采取措施保证正确性,这就是线程安全。主要手段有同步机制、原子操作类、并发容器等。
-
同步容器与并发容器:同步容器使用同步锁保证线程安全,效率低;并发容器使用高级锁和算法保证线程安全,效率高。
-
lock锁:主要有互斥锁、读写锁、定时锁、条件锁等,用于控制线程对共享资源的访问。
-
volatile、CAS、atomic:保证变量的可见性、有序性和原子性。
-
并发工具类:Semaphore信号量、CountDownLatch计数器、CyclicBarrier栅栏、BlockingQueue阻塞队列等。
-
Java内存模型:定义了主内存和工作内存(本地内存)之间的抽象关系,以及线程对内存的访问规则。
-
Java对象结构:包含对象头、实例数据和数组长度三部分,部分用于存储同步信息。
-
AQS:AbstractQueuedSynchronizer,Java并发包底层同步器,许多并发类基于此扩展实现。用state状态和CLH队列维护同步状态。
-
ConcurrentHashMap:高效的线程安全HashMap,使用分段锁和CAS来保证线程安全。
-
@Async:基于线程池实现异步方法调用,避免手动创建和管理线程。可以自定义线程池的使用。