Unsafe,提供给Java直接操作内存的方法,是极不安全的,不受JVM管理,容易出现内存泄露。Unsafe使用了单例模式,通过静态getUnsafe()方法获取Unsafe对象。
LockSupport 底层是基于Usafe,实现线程的挂起和唤醒的工具类。内部方法都是静态的,可以在任何地方任何时候进行调用。LockSupport一般也不太使用,也是因为它底层也是直接对Unsafe对象的封装,使用起来还是比较危险的。
其主要方法:
public static void park() {
U.park(false, 0L);
}
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
U.park(false, 0L);
setBlocker(t, null);
}
public static void parkNanos(long nanos) {
if (nanos > 0)
U.park(false, nanos);
}
public static void parkNanos(Object blocker, long nanos) {
if (nanos > 0) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
U.park(false, nanos);
setBlocker(t, null);
}
}
public static void unpark(Thread thread) {
if (thread != null)
U.unpark(thread);
}
LockSupport类与每个使用它的线程都会关联一个许可证,默认情况下调用LockSupport类的方法等线程是不持有许可证等。当一个线程调用了park()方法,会将自己阻塞挂起,不再参与线程的调度。在其他线程调用unpark(Thread thread) 方法并且将调用park方法的线程作为参数时,调用park方法而被阻塞的线程会被唤醒返回。
如果一个线程调用unpark方法时,参数thread线程没有持有thread与LockSupport类关联的许可,则让thread线程持有。如果thread之前因调用park()而被挂起,则调用unpark后,参数thread线程会被唤醒。如果thread之前没有调用park(),则调用unpark()之后,参数线程thread再调用park方法,会立刻返回。也就是说park()与unpark()调用不分先后。
注意:park()与unpark()应成对出现,即一个线程执行park()之后,必须有一个线程执行unpark()操作(也可以自己唤醒自己,但是需要unpark()先执行,再执行park()操作,但是这样并没有什么意义),否则执行park操作的线程将会永久的被挂起(也可以通过使用parkNanos(long nanos)在挂起数秒后自动唤醒)。虽然unpark操作可以在park()操作之前执行,看似很灵活,但要注意一个unpark操作只会唤醒一次park()操作,且执行unpark()操作的线程,必须在执行park()操作的线程返回之后才能结束,否则执行park操作的线程将跟着一起结束。
一次unpark()操作只会唤醒一次park()操作:
@Test
public void LockSupportTest() throws InterruptedException{
ParkThread thread = new ParkThread();
thread.start();
LockSupport.unpark(thread);
System.out.println("主线程休眠10s……");
Thread.sleep(10 * 1000);
System.out.println("主线程允许结束……");
}
class ParkThread extends Thread{
@Override
public void run() {
System.out.println("ParkThread is begin……");
LockSupport.park();
System.out.println("子线程第一次被唤醒……");
try{
// 线程休眠 2S,等到主线程先执行完 unpark()方法
Thread.sleep(2 * 1000);
LockSupport.park();
System.out.println("子线程第二次被唤醒……");
}catch (InterruptedException e){
System.out.println(e);
}
System.out.println("子线程运行结束……");
}
}
//运行结果///
ParkThread is begin……
主线程休眠10s……
子线程第一次被唤醒……
主线程允许结束……
子线程在第一次唤醒之后,第二次执行park()操作仍然被挂起,并没有得到执行,未正常执行结束。
unpark()操作的线程必须在park()操作的线程之后结束:
@Test
public void LockSupportTest() throws InterruptedException{
ParkThread thread = new ParkThread();
thread.start();
LockSupport.unpark(thread);
System.out.println("主线程休眠1s……");
Thread.sleep(2 * 1000);
//thread.join();
System.out.println("主线程允许结束……");
}
class ParkThread extends Thread{
@Override
public void run() {
try{
System.out.println("ParkThread is begin……");
LockSupport.park();
System.out.println("子线程被唤醒……");
Thread.sleep(5 * 1000);
}catch (InterruptedException e){
System.out.println(e);
}
System.out.println("子线程运行结束……");
}
}
//运行结果///
ParkThread is begin……
主线程休眠1s……
子线程被唤醒……
主线程运行结束……
thread.join();运行结果///
ParkThread is begin……
主线程休眠1s……
子线程被唤醒……
子线程运行结束……
主线程允许结束……
当主线程运行结束之后,程序也就退出,但是子线程并没有正常执行结束,这种情况可以配合join()一起使用。
在挂起线程时,建议使用带有blocker参数的方法:park(Object blocker)或parkNanos(Object blocker, long nanos),当现在没有持有许可被挂起的情况下,blocker对象会被记录在该线程内部,使用stack pid命令查看堆栈时可以看到被挂起线程的信息。