LockSupport 线程控制 & Unsafe 核心类
一、概述
LockSupport类,JUC包 中的一个工具类,用来创建锁和其他同步类的基本线程阻塞原语。
Basic thread blocking primitives for creating locks and other synchronization classes
LockSupport类 的核心方法其实就两个:park() 和 unpark() ,其中 park() 方法用来阻塞当前调用线程,unpark() 方法用于唤醒指定线程。 这其实和 Object 类的 wait() 和 signal() 方法有些类似,但是 LockSupport类 的这两种方法从语意上讲比 Object类 的方法更清晰,而且可以针对指定线程进行阻塞和唤醒。
LockSupport类使用了一种名为Permit(许可)的概念来做到阻塞和唤醒线程的功能,可以把许可看成是一种(0,1)信号量(Semaphore),但与 Semaphore 不同的是,许可的累加上限是1。 初始时,permit为0,当调用unpark()方法时,线程的permit加1,当调用park()方法时,如果permit为0,则调用线程进入阻塞状态。
二、LockSupport 线程控制
1. 使用方法
LockSupport两个核心方法:
public static void park() {
U.park(false, 0L);
}
public static void unpark(Thread thread) {
if (thread != null)
U.unpark(thread);
}
举例测试如何阻塞线程:
import java.util.concurrent.locks.LockSupport;
public class UseLockSupport {
public static void main(String[] args) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程 " + Thread.currentThread().getName() + " 开始执行执行并进行park等待");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
LockSupport.park();
System.out.println("线程 " + Thread.currentThread().getName() + " 解除park等待继续执行");
}
});
t.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
LockSupport.unpark(t);
}
}
运行结果:
2. 底层实现
LockSupport 对线程的控制基于 Unsafe 类的 线程调度 功能:
public static void park() {
U.park(false, 0L);
}
public static void unpark(Thread thread) {
if (thread != null)
U.unpark(thread);
}
三、Unsafe 核心类
Unsafe类使Java拥有了像C语言的指针一样操作内存空间的能力:
- 不受 JVM 管理,也就意味着无法被 GC,需要我们手动 GC,稍有不慎就会出现内存泄漏。
- Unsafe 部分方法中必须提供原始地址(内存地址)和被替换对象的地址,偏移量需程序员计算,若出现问题会导致整个JVM实例崩溃,应用程序也会随之崩溃
- 直接操作内存,其速度更快,在高并发的条件下能很好地提高效率。
1. 创建对象
Unsafe类是final 的,不允许继承。
在JDK中,创建 sun.misc.Unsafe 对象 需要调用 getUnsafe() 方法,它采用了单例模式,保证全局仅有一个对象:
private Unsafe() {}
private static final Unsafe theUnsafe = new Unsafe();
@CallerSensitive
public static Unsafe getUnsafe() {
Class<?> caller = Reflection.getCallerClass();
if (!VM.isSystemDomainLoader(caller.getClassLoader()))
throw new SecurityException("Unsafe");
return theUnsafe;
}
获取 Unsafe 类对象的条件:当前类加载器为 BootStrapLoader ,否则会抛出异常:
为了获取到 sun.misc.Unsafe 对象,我们可以使用 反射 直接获取内部定义的单例:
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
Unsafe unsafe = (Unsafe) f.get(null);
而 sun.misc.Unsafe 内部依赖于 jdk.internal.misc.Unsafe ,但我们不能直接获取jdk.internal.misc.Unsafe 对象(包括反射,因为编译不通过)。
2. 内置方法
我们对方法的展示来自于 jdk.internal.misc.Unsafe 类
① 普通读写
通过 Unsafe 内置方法可以对任何对象进行读写,无论它私有与否:
@HotSpotIntrinsicCandidate
public native int getInt(Object o, long offset);
@HotSpotIntrinsicCandidate
public native void putInt(Object o, long offset, int x);
除此之外,还可以直接在地址上读写:
@ForceInline
public long getAddress(Object o, long offset) {
if (ADDRESS_SIZE == 4) {
return Integer.toUnsignedLong(getInt(o, offset));
} else {
return getLong(o, offset);
}
}
@ForceInline
public void putAddress(Object o, long offset, long x) {
if (ADDRESS_SIZE == 4) {
putInt(o, offset, (int)x);
} else {
putLong(o, offset, x);
}
}
除了基本数据类型以外,还提供了引用类型的读写方法:
@Deprecated(since="12", forRemoval=true)
public final Object getObject(Object o, long offset) {
return getReference(o, offset);
}
@Deprecated(since="12", forRemoval=true)
public final void putObject(Object o, long offset, Object x) {
putReference(o, offset, x);
}
遗憾的是,在JDK12 以后,这些引用类型读写方法已经 “过时” 了,并不推荐使用,取而代之的是这些方法:
@HotSpotIntrinsicCandidate
public native Object getReference(Object o, long offset);
@HotSpotIntrinsicCandidate
public native void putReference(Object o, long offset, Object x);
② volatile 读写
普通的读写无法保证可见性 和有序性 ,而 volatile读写 方法则避免了该问题: 基本数据类型:
@HotSpotIntrinsicCandidate
public native void putIntVolatile(Object o, long offset, int x);
@HotSpotIntrinsicCandidate
public native int getIntVolatile(Object o, long offset);
引用类型:
@HotSpotIntrinsicCandidate
public native Object getReferenceVolatile(Object o, long offset);
@HotSpotIntrinsicCandidate
public native void putReferenceVolatile(Object o, long offset, Object x);
③ 有序写入
有序写入只保证写入的有序性,不保证可见性,就是说一个线程的写入不保证其他线程立马可见。
@ForceInline
public void putOrderedInt(Object o, long offset, int x) {
theInternalUnsafe.putIntRelease(o, offset, x);
}
而本方法依赖于方法,完成有序写入:
@HotSpotIntrinsicCandidate
public final void putIntRelease(Object o, long offset, int x) {
putIntVolatile(o, offset, x);
}
除此之外还有其他类型、引用类型对应的方法,在此不进行列举。
④ CAS 操作
JUC中大量运用了 CAS操作, CAS操作 是 JUC 的基础。Unsafe中提供了 多种类型 的CAS操作:
@HotSpotIntrinsicCandidate
public final native boolean compareAndSetInt(Object o, long offset,
int expected,
int x);
}
@HotSpotIntrinsicCandidate
public final native boolean compareAndSetReference(Object o, long offset,
Object expected,
Object x);
⑤ 直接内存操作
Unsafe 类为我们提供了直接操作内存的能力,并且这些方法依赖于一些 native 本地方法,为了区分,本地方法以 数字 0 结尾。
分配内存
public long allocateMemory(long bytes) {
bytes = alignToHeapWordSize(bytes);
allocateMemoryChecks(bytes);
if (bytes == 0) {
return 0;
}
long p = allocateMemory0(bytes);
if (p == 0) {
throw new OutOfMemoryError("Unable to allocate " + bytes + " bytes");
}
return p;
}
重新分配内存
public long reallocateMemory(long address, long bytes) {
bytes = alignToHeapWordSize(bytes);
reallocateMemoryChecks(address, bytes);
if (bytes == 0) {
freeMemory(address);
return 0;
}
long p = (address == 0) ? allocateMemory0(bytes) : reallocateMemory0(address, bytes);
if (p == 0) {
throw new OutOfMemoryError("Unable to allocate " + bytes + " bytes");
}
return p;
}
内存初始化
public void setMemory(Object o, long offset, long bytes, byte value) {
setMemoryChecks(o, offset, bytes, value);
if (bytes == 0) {
return;
}
setMemory0(o, offset, bytes, value);
}
内存复制
public void copyMemory(Object srcBase, long srcOffset,
Object destBase, long destOffset,
long bytes) {
copyMemoryChecks(srcBase, srcOffset, destBase, destOffset, bytes);
if (bytes == 0) {
return;
}
copyMemory0(srcBase, srcOffset, destBase, destOffset, bytes);
}
清除内存
public void freeMemory(long address) {
freeMemoryChecks(address);
if (address == 0) {
return;
}
freeMemory0(address);
}
⑥ 线程调度
@HotSpotIntrinsicCandidate
public native void park(boolean isAbsolute, long time);
@HotSpotIntrinsicCandidate
public native void unpark(Object thread);
⑦ 内存屏障
- loadFence:保证在这个屏障之前的所有
读操作 都已经完成。 - storeFence:保证在这个屏障之前的所有
写操作 都已经完成。 - fullFence:保证在这个屏障之前的所有
读写操作 都已经完成。
@HotSpotIntrinsicCandidate
public native void loadFence();
@HotSpotIntrinsicCandidate
public native void storeFence();
@HotSpotIntrinsicCandidate
public native void fullFence();
3. 特殊应用
① 无视构造方法
public static void main(String[] args) throws Exception {
Unsafe unsafe = null;
try{
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
unsafe = (Unsafe) f.get(null);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
A o1 = new A();
o1.a();
A o2 = A.class.getDeclaredConstructor().newInstance();
o2.a();
A o3 = (A) unsafe.allocateInstance(A.class);
o3.a();
}
static class A {
private long a;
public A() {
this.a = 1;
}
public long a() {
System.out.println(this.a);
return this.a;
}
}
运行结果:
② 巨型数组
private static Unsafe unsafe;
public static void main(String[] args) throws Exception {
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
unsafe = (Unsafe) f.get(null);
long SUPER_SIZE = (long) Integer.MAX_VALUE * 2;
SuperArray array = new SuperArray(SUPER_SIZE);
System.out.println("Array size:" + array.size());
int sum = 0;
for (int i = 0; i < 100; i++) {
array.set((long) Integer.MAX_VALUE + i, (byte) 3);
sum += array.get((long) Integer.MAX_VALUE + i);
}
System.out.println("Sum of 100 elements:" + sum);
}
private static Unsafe getUnsafe() {
return unsafe;
}
static class SuperArray {
private final static int BYTE = 1;
private long size;
private long address;
public SuperArray(long size) {
this.size = size;
address = getUnsafe().allocateMemory(size * BYTE);
}
public void set(long i, byte value) {
getUnsafe().putByte(address + i * BYTE, value);
}
public int get(long idx) {
return getUnsafe().getByte(address + idx * BYTE);
}
public long size() {
return size;
}
}
运行结果:
|