05原子类和Unsafe

Atomic

Java 中的 java.util.concurrent.atomic 包提供了一系列类,这些类支持原子操作(即线程安全而无需同步)在单个变量上,这大大减少了并发编程的复杂性。

原子操作类主要有这些:

  1. 原子操作的基本数据类型:AtomicBoolean、AtomicInteger、AtomicLong;
  2. 原子操作的数组类型:AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray;
  3. 原子操作的引用类型:AtomicReference、AtomicReferenceFieldUpdater、AtomicMarkableReference;

Unsafe

Java 魔法类 Unsafe 详解 | JavaGuide

面试

1. 什么是Unsafe?

Unsafe 是 Java 中的一个类,它提供了一些底层操作的方法,可以绕过 Java 的安全检查机制直接操作内存和对象。它是在 sun.misc 包下的一个非常特殊的类,主要用于支持 JDK 内部的实现。

2. 为什么需要Unsafe?

Java 是一门相对安全的语言,它提供了很多安全机制来保护开发者免受潜在的危险。然而,在某些情况下,我们可能需要绕过这些安全机制,直接进行一些底层的操作,比如修改对象的字段值、创建实例等。这时就可以使用 Unsafe 类来完成这些操作。

Unsafe 类的存在主要是为了支持 JDK 内部的实现,比如 CAS(Compare and Swap)操作、原子性操作等。同时,它也被广泛应用于一些高性能框架和库中,比如 Netty、Hadoop 等。

3. Unsafe 的实现原理?

Unsafe 类通过本地方法来实现其功能,它调用了 JVM 提供的底层函数来完成一些不安全的操作。由于 Unsafe 类的方法都是 native 方法,所以无法直接查看其源码。

Unsafe 类的实现依赖于 JVM 的具体实现,不同的 JVM 可能会有不同的实现方式。通常情况下,Unsafe 类会使用一些特殊的指令来绕过 Java 的安全检查机制,直接操作内存和对象。

4. Unsafe 的使用示例

由于 Unsafe 类是一个非常底层的类,它的使用需要谨慎,并且不推荐在生产环境中使用。下面是一个简单的示例,演示了如何使用 Unsafe 类来修改对象的字段值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import sun.misc.Unsafe;

public class UnsafeExample {
private int value = 10;

public static void main(String[] args) throws Exception {
Unsafe unsafe = getUnsafe();

long offset = unsafe.objectFieldOffset(UnsafeExample.class.getDeclaredField("value"));
UnsafeExample example = new UnsafeExample();

System.out.println("Before update: " + example.value);

unsafe.putInt(example, offset, 20);

System.out.println("After update: " + example.value);
}

private static Unsafe getUnsafe() throws Exception {
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
return (Unsafe) field.get(null);
}
}

上述代码首先通过反射获取到 Unsafe 实例,然后使用 objectFieldOffset 方法获取到字段的偏移量,最后调用 putInt 方法修改字段的值。运行该代码,输出结果为:

1
2
Before update: 10
After update: 20

可以看到,通过 Unsafe 类成功地修改了对象的字段值。

5. Unsafe 的优点

  • 灵活性:Unsafe 类提供了一些底层操作的方法,可以绕过 Java 的安全检查机制,直接进行一些底层的操作。这使得开发者能够更加灵活地处理一些特殊情况。
  • 性能:由于 Unsafe 类绕过了 Java 的安全检查机制,直接操作内存和对象,因此在某些场景下可以提供更高的性能。

6. Unsafe 的缺点

  • 不安全性:Unsafe 类的存在破坏了 Java 的安全性,可能导致一些潜在的风险。使用 Unsafe 类需要非常小心,并且只在必要的情况下使用。
  • 平台依赖性:Unsafe 类的实现依赖于 JVM 的具体实现,不同的 JVM 可能会有不同的实现方式。这意味着代码在不同的 JVM 上可能会有不同的行为。

7. Unsafe 的使用注意事项

  • 谨慎使用:Unsafe 类是一个非常底层的类,它的使用需要谨慎,并且不推荐在生产环境中使用。只有在确保安全性的前提下,才应该考虑使用 Unsafe 类。
  • 了解底层原理:使用 Unsafe 类需要对底层的内存模型和对象布局有一定的了解。如果没有足够的了解,可能会导致一些难以调试和解决的问题。
  • 平台兼容性:由于 Unsafe 类的实现依赖于 JVM 的具体实现,不同的 JVM 可能会有不同的行为。在使用 Unsafe 类时,需要考虑代码在不同的 JVM 上的兼容性。

Java 并发工具你知道哪些?

Java 中一些常用的并发工具,它们位于 java.util.concurrent 包中,常见的有:

  • CountDownLatch:CountDownLatch 是一个同步辅助类,它允许一个或多个线程等待其他线程完成操作。它使用一个计数器进行初始化,调用 countDown() 方法会使计数器减一,当计数器的值减为 0 时,等待的线程会被唤醒。可以把它想象成一个倒计时器,当倒计时结束(计数器为 0)时,等待的事件就会发生。示例代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import java.util.concurrent.CountDownLatch;

public class CountDownLatchExample {
public static void main(String[] args) throws InterruptedException {
int numberOfThreads = 3;
CountDownLatch latch = new CountDownLatch(numberOfThreads);

// 创建并启动三个工作线程
for (int i = 0; i < numberOfThreads; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " 正在工作");
try {
Thread.sleep(1000); // 模拟工作时间
} catch (InterruptedException e) {
e.printStackTrace();
}
latch.countDown(); // 完成工作,计数器减一
System.out.println(Thread.currentThread().getName() + " 完成工作");
}).start();
}

System.out.println("主线程等待工作线程完成");
latch.await(); // 主线程等待,直到计数器为 0
System.out.println("所有工作线程已完成,主线程继续执行");
}
}
  • CyclicBarrier:CyclicBarrier 允许一组线程互相等待,直到到达一个公共的屏障点。当所有线程都到达这个屏障点后,它们可以继续执行后续操作,并且这个屏障可以被重置循环使用。与 CountDownLatch 不同,CyclicBarrier 侧重于线程间的相互等待,而不是等待某些操作完成。示例代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierExample {
public static void main(String[] args) {
int numberOfThreads = 3;
CyclicBarrier barrier = new CyclicBarrier(numberOfThreads, () -> {
System.out.println("所有线程都到达了屏障,继续执行后续操作");
});

for (int i = 0; i < numberOfThreads; i++) {
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + " 正在运行");
Thread.sleep(1000); // 模拟运行时间
barrier.await(); // 等待其他线程
System.out.println(Thread.currentThread().getName() + " 已经通过屏障");
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
}
}
  • Semaphore:Semaphore 是一个计数信号量,用于控制同时访问某个共享资源的线程数量。通过 acquire() 方法获取许可,使用 release() 方法释放许可。如果没有许可可用,线程将被阻塞,直到有许可被释放。可以用来限制对某些资源(如数据库连接池、文件操作等)的并发访问量。代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import java.util.concurrent.Semaphore;

public class SemaphoreExample {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(2); // 允许 2 个线程同时访问

for (int i = 0; i < 5; i++) {
new Thread(() -> {
try {
semaphore.acquire(); // 获取许可
System.out.println(Thread.currentThread().getName() + " 获得了许可");
Thread.sleep(2000); // 模拟资源使用
System.out.println(Thread.currentThread().getName() + " 释放了许可");
semaphore.release(); // 释放许可
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
}

CountDownLatch 是做什么的讲一讲?

CountDownLatch 是 Java 并发包(java.util.concurrent)中的一个同步工具类,用于让一个或多个线程等待其他线程完成操作后再继续执行

其核心是通过一个计数器(Counter)实现线程间的协调,常用于多线程任务的分阶段控制或主线程等待多个子线程就绪的场景,核心原理:

  • 初始化计数器:创建 CountDownLatch 时指定一个初始计数值(如 N)。
  • 等待线程阻塞:调用 await() 的线程会被阻塞,直到计数器变为 0。
  • 任务完成通知:其他线程完成任务后调用 countDown(),使计数器减 1。
  • 唤醒等待线程:当计数器减到 0 时,所有等待的线程会被唤醒。

05原子类和Unsafe
http://example.com/2025/11/14/05原子类和Unsafe/
作者
無鎏雲
发布于
2025年11月14日
许可协议