赛博朔方 枫叶
LockSupport提供park()和unpark()方法实现阻塞线程和解除线程阻塞,实现的阻塞和解除阻塞是基于”许可(permit)”作为关联,permit相当于一个信号量(0,1),默认是0. 线程之间不再需要一个Object或者其它变量来存储状态,不再需要关心对方的状态.
源码:public class LockSupport {
private LockSupport() {} // Cannot be instantiated.
private static void setBlocker(Thread t, Object arg) {
// Even though volatile, hotspot doesn't need a write barrier here.
UNSAFE.putObject(t, parkBlockerOffset, arg);
}
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(false, 0L);
setBlocker(t, null);
}
public static void parkNanos(Object blocker, long nanos) {
if (nanos > 0) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(false, nanos);
setBlocker(t, null);
}
}
public static void parkUntil(Object blocker, long deadline) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(true, deadline);
setBlocker(t, null);
}
public static Object getBlocker(Thread t) {
if (t == null)
throw new NullPointerException();
return UNSAFE.getObjectVolatile(t, parkBlockerOffset);
}
public static void park() {
UNSAFE.park(false, 0L);
}
public static void parkNanos(long nanos) {
if (nanos > 0)
UNSAFE.park(false, nanos);
}
public static void parkUntil(long deadline) {
UNSAFE.park(true, deadline);
}
static final int nextSecondarySeed() {
int r;
Thread t = Thread.currentThread();
if ((r = UNSAFE.getInt(t, SECONDARY)) != 0) {
r ^= r << 13; // xorshift
r ^= r >>> 17;
r ^= r << 5;
}
else if ((r = java.util.concurrent.ThreadLocalRandom.current().nextInt()) == 0)
r = 1; // avoid zero
UNSAFE.putInt(t, SECONDARY, r);
return r;
}
// Hotspot implementation via intrinsics API
private static final sun.misc.Unsafe UNSAFE;
private static final long parkBlockerOffset;
private static final long SEED;
private static final long PROBE;
private static final long SECONDARY;
static {
try {
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class> tk = Thread.class;
parkBlockerOffset = UNSAFE.objectFieldOffset
(tk.getDeclaredField("parkBlocker"));
SEED = UNSAFE.objectFieldOffset
(tk.getDeclaredField("threadLocalRandomSeed"));
PROBE = UNSAFE.objectFieldOffset
(tk.getDeclaredField("threadLocalRandomProbe"));
SECONDARY = UNSAFE.objectFieldOffset
(tk.getDeclaredField("threadLocalRandomSecondarySeed"));
} catch (Exception ex) { throw new Error(ex); }
}
}
2.方法都是静态方法
UNSAFE
parkBlockerOffset
SEED
PROBE
SECONDARY
UNSAFE:
JDK内部用的工具类, 可以直接操控内存,被JDK广泛用于自己的包中.它通过暴露一些Java意义上说“不安全”的功能给Java层代码,来让JDK能够更多的使用Java代码来实现一些原本是平台相关的、需要使用native语言(例如C或C++)才可以实现的功能。该类不应该在JDK核心类库之外使用。
Class> tk = Thread.class;
parkBlockerOffset = UNSAFE.objectFieldOffset
(tk.getDeclaredField("parkBlocker"));
/**
* The argument supplied to the current call to
* java.util.concurrent.locks.LockSupport.park.
* Set by (private) java.util.concurrent.locks.LockSupport.setBlocker
* Accessed using java.util.concurrent.locks.LockSupport.getBlocker
*/
volatile Object parkBlocker;
parkBlockerOffset = UNSAFE.objectFieldOffset
(tk.getDeclaredField("parkBlocker"));
原来偏移量就算Thread这个类里面变量parkBlocker在内存中的偏移量:
JVM的实现可以自由选择如何实现Java对象的“布局“,也就是在内存里Java对象的各个部分放在哪里,包括对象的实例字段和一些元数据之类。 sun.misc.Unsafe里关于对象字段访问的方法把对象布局抽象出来,它提供了objectFieldOffset()方法用于获取某个字段相对 Java对象的“起始地址”的偏移量,也提供了getInt、getLong、getObject之类的方法可以使用前面获取的偏移量来访问某个Java 对象的某个字段。
/** The current seed for a ThreadLocalRandom */
@sun.misc.Contended("tlr")
long threadLocalRandomSeed;
/** Probe hash value; nonzero if threadLocalRandomSeed initialized */
@sun.misc.Contended("tlr")
int threadLocalRandomProbe;
/** Secondary seed isolated from public ThreadLocalRandom sequence */
@sun.misc.Contended("tlr")
int threadLocalRandomSecondarySeed;
都是Thread类中的内存偏移地址,主要用于ThreadLocalRandom类进行随机数生成,它要比Random性能好很多,可阅读了解详情
核心函数
因为LockSupport的核心方法都是基于Unsafe类中的park和unpark方法
public native void park(boolean isAbsolute, long time);
public native void unpark(Thread thread);
public static void park();
public static void park() {
UNSAFE.park(false, 0L);// // 获取许可,设置时间为无限长,直到可以获取许可
}
public static void park(Object blocker);
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(false, 0L);
setBlocker(t, null);
}
private static void setBlocker(Thread t, Object arg) {
UNSAFE.putObject(t, parkBlockerOffset, arg);
}
public static void parkNanos(Object blocker, long nanos)
public static void parkNanos(Object blocker, long nanos) {
if (nanos > 0) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(false, nanos);
setBlocker(t, null);
}
}
public static void parkUntil(Object blocker, long deadline)
public static void parkUntil(Object blocker, long deadline) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(true, deadline);
setBlocker(t, null);
}
public static void unpark(Thread thread)
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}
线程在 park 上受阻塞,将解除其阻塞状态。否则,预发许可,下一次调用 park 不会受阻塞
2.实现机制不同。虽然LockSuport可以指定monitor的object对象,但和object.wait(),两者的阻塞队列并不交叉。
public class LockSupportTest {
public static void main(String[] args) throws Exception {
TestLockSupport lockSupport = new TestLockSupport();
lockSupport.start();
// TestThread thread = new TestThread();
// thread.start();
}
}
class TestLockSupport extends Thread {
public void run() {
System.out.println( "TestLockSupport.run()" );
LockSupport.park( );
}
}
class TestThread extends Thread {
public void run() {
System.out.println( "TestThread.run()" );
synchronized (this) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
"Thread-0" #10 prio=5 os_prio=0 tid=0x000000001d6a5800 nid=0x2ad0 waiting on con
dition [0x000000001e1cf000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:304)
at TestLockSupport.run(LockSupportTest.java:16)
"Thread-0" #10 prio=5 os_prio=0 tid=0x000000001d321000 nid=0x8a8 in Object.wait(
) [0x000000001e2af000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076b257698> (a TestThread)
at java.lang.Object.wait(Object.java:502)
at TestThread.run(LockSupportTest.java:23)
- locked <0x000000076b257698> (a TestThread)
结论:
Thread的notify也是在实际使用过程只需要斟酌的事情,notifyAll唤醒所有线程;notify唤醒单个线程,如果有两个被阻塞线程….
LockSupport的park也可以响应Thread的interrupt中断,后面会有例子详讲.public class LockSupportTest {
public static void main(String[] args) throws Exception {
TestThread thread = new TestThread( Thread.currentThread() );
thread.start();
System.out.println( "before park" );
// 等待获取许可
LockSupport.park( "Park" );
System.out.println( "after park" );
}
}
class TestThread extends Thread {
private Object object;
public TestThread(Object object) {
this.object = object;
}
public void run() {
System.out.println( "before unpark" );
// 休眠,保证setBlocker(t, blocker)
try {
Thread.sleep( 500 );
} catch (InterruptedException e) {
e.printStackTrace();
}
// 获取blocker
System.out.println( "Blocker info: " + LockSupport.getBlocker( (Thread) object ) );
// 释放许可
LockSupport.unpark( (Thread) object );
// 休眠500ms,保证先执行park中的setBlocker(t, null);
try {
Thread.sleep( 500 );
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println( "Blocker info: " + LockSupport.getBlocker( (Thread) object ) );
System.out.println( "after unpark" );
}
}
before park
before unpark
Blocker info Park
after park
Blocker info null
after unpark
先执行park,然后在执行unpark,进行同步,并且在unpark的前后都调用了getBlocker,可以看到两次的结果不一样,第二次调用的结果为null,这是因为在调用unpark之后,执行了Lock.park(Object blocker) 方法中的setBlocker(t, null) 方法,所以第二次调用getBlocker时为null
public class LockSupportTest {
public static void main(String[] args) throws Exception {
TestThread thread = new TestThread( Thread.currentThread() );
long start = System.nanoTime();
thread.start();
try {
Thread.sleep( 1000 );
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println( "before park" );
// 等待获取许可
LockSupport.park( "Park" );
System.out.println( "after park:" + (System.nanoTime() - start) );
}
}
class TestThread extends Thread {
private Object object;
public TestThread(Object object) {
this.object = object;
}
public void run() {
System.out.println( "before unpark" );
LockSupport.unpark( (Thread) object );
System.out.println( "after unpark" );
}
}
before unpark
after unpark
before park
after park:3000046392
先执行unpark,在调用park,直接就没被阻塞, 因此park/unpark相比wait/notify更加的灵活
public class LockSupportTest {
public static void main(String[] args) throws Exception {
TestThread thread = new TestThread();
thread.start();
try {
Thread.sleep( 1000 );
} catch (InterruptedException e) {
e.printStackTrace();
}
thread.interrupt();
}
}
class TestThread extends Thread {
public void run() {
System.out.println( Thread.currentThread().isInterrupted() );
LockSupport.park();
System.out.println( Thread.currentThread().isInterrupted() );
}
}
false
true
可以看到子线程被阻塞后,主线程调用子线程中断,子线程则继续执行,并没有抛出InterruptedException, 这里依赖的是Interrupted status, 如果线程被中断而退出阻塞该状态会被修改为true, 可见interrupt起到的作用与unpark一样.