JAVA并发包(二):LockSupport

本文深入解析LockSupport类在Java并发编程中的应用,包括其挂起和解除挂起方法,以及与Object.wait方法的区别。通过实例演示如何使用LockSupport进行线程控制。

LockSupport是挂起线程的一种实现方式,它不是同步锁,只是一个提供给同步锁调用,把当前线程挂起的协助类。它也能够单独使用,把某个线程挂起。

一、方法介绍

LockSupport的方法有以下几个,后面会一个个介绍
在这里插入图片描述

1.挂起方法

1.1 park()

这个方法很简答,直接调用了UNSAFE类的原生方法,可以立即它会立刻把当前线程挂起。注意它是一个静态方法,直接通过类名引用就可以调用


    public static void park() {
        UNSAFE.park(false, 0L);
    }
    // UNSAFE类里的方法
	public native void park(boolean var1, long var2);

1.2 park(Object blocker)

跟无参的park相比,传入的对象可以记录在Thread的parkBlocker变量中,当调用getBlocker方法时,可以获取到这个传入的对象。

	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);
    }

	Class<?> tk = Thread.class;
	private static final long parkBlockerOffset; = UNSAFE.objectFieldOffset
                (tk.getDeclaredField("parkBlocker"));

	public static Object getBlocker(Thread t) {
        if (t == null)
            throw new NullPointerException();
        return UNSAFE.getObjectVolatile(t, parkBlockerOffset);
    }

1.3 parkUntil(Object blocker, long deadline)

这个方法,可以挂起线程直到某个时间点,注意deadline是绝对时间,即未来的某个时间点的时间戳

 	public static void parkUntil(Object blocker, long deadline) {
        Thread t = Thread.currentThread();
        setBlocker(t, blocker);
        UNSAFE.park(true, deadline);
        setBlocker(t, null);
    }

2.解除挂起方法

2.1 正常解除挂起

解除挂起只有一个方法,直接调用UNSAFE的方法。

	public static void unpark(Thread thread) {
        if (thread != null)
            UNSAFE.unpark(thread);
    }

2.2 被其他线程干扰

当其他线程调用了挂起线程的interrupt()方法,也会把挂起线程唤醒

2.3 其他未知原因

挂起的线程也会有未知的意外会被唤醒,所以如果我们对这个挂起操作比较依赖时,要把挂起方法放到轮询的方法里,比如:
while(挂起条件成立){
LockSupport.park();
}

二、使用实例

如下例子中,我开启了两个线程去调用同步锁方法,当获取锁后调用park方法,看看它被挂起后会不会释放锁。从例子可以看出,park方法挂起时是不会释放锁的,这点跟Object的wait方法不一样,wait方法会释放锁。

	import java.util.concurrent.locks.LockSupport;

	public class LockSupportTest {
	public static void main(String[] args) throws InterruptedException {
        LockSupportTest test = new LockSupportTest();

        Thread t1 = new Thread(() -> {
            System.out.println("线程1启动了");
            test.lock("线程1");
        });
        t1.start();

        Thread.sleep(1 * 1000);

        Thread t2 =new Thread(() -> {
            System.out.println("线程2启动了");
            test.lock("线程2");
        });
        t2.start();

        Thread.sleep(1 * 1000);

        LockSupport.unpark(t1);

        Thread.sleep(1 * 1000);

        LockSupport.unpark(t2);
    }
    
    public  synchronized void lock(String threadName){
        System.out.println("加锁开始,线程名=" + threadName);
        LockSupport.park();
        System.out.println("加锁结束,线程名=" + threadName);
    }
}

在这里插入图片描述
wait方法释放锁的例子如下,注意不管是wait还是notifyAll方法调用前都要首先获得同步锁。

		LockSupportTest test = new LockSupportTest();

        Thread t1 = new Thread(() -> {
            System.out.println("线程1启动了");
            synchronized (test){
                while (flag == 0){
                    try {
                        test.wait();
                    } catch (InterruptedException e) {

                    }
                }
            }

            System.out.println("线程1结束了");
        });
        t1.start();

        Thread.sleep(1 * 1000);

        Thread t2 =new Thread(() -> {
            System.out.println("线程2启动了");
            synchronized (test){
                while (flag == 0){
                    try {
                        test.wait();
                    } catch (InterruptedException e) {

                    }
                }
            }

            System.out.println("线程2结束了");
        });
        t2.start();

        Thread.sleep(2000);

        // 更新等待标志位,被唤醒的线程才会退出
        flag = 1;
        synchronized (test){
            test.notifyAll();
        }
        t1.join();
        t2.join();

在这里插入图片描述

三、总结

  1. LockSupport使用场景是挂起线程,如果线程占有锁,挂起时不会释放锁。它与Object的wait方法的区别就这在于这里,wait方法会释放锁。
  2. LockSupport的park和unpark方法调用上没有顺序的要求,也就是说可以先调用,unpark再调用park。可以把它理解为古代的虎符,成对出现时才可以调兵调兵遣将。park和unpark成对出现时线程就可以继续运行。但是不能多次调用unpark,然后试图多次调用park来让线程运行。比如下面这样的代码,
LockSupport.unpark(Thread.currentThread());
LockSupport.unpark(Thread.currentThread());
LockSupport.unpark(Thread.currentThread());

// park1
LockSupport.park();
// park2 程序最终会在park2这里挂起
LockSupport.park();
// park3
LockSupport.park();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值