线程等待唤醒机制

Object 的wait()方法和notify()方法

  public static void main(String[] args) {

        Object objectLock = new Object();

        new Thread(() -> {
            synchronized (objectLock) {
                try {
                    System.out.println(Thread.currentThread().getName() + ",come in");
                    objectLock.wait();
                    System.out.println(Thread.currentThread().getName() + ",被唤醒");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "t1").start();

        //暂停几秒,保证t1线程先进入synchronized代码块
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            synchronized (objectLock) {
                objectLock.notify();
                System.out.println(Thread.currentThread().getName() + ",发出通知");
            }

        }, "t2").start();
    }

        打印结果:

t1,come in
t2,发出通知
t1,被唤醒

        现在案例是我们先让t1获得objectLock,t1紧接着调用objectLock的wait()方法交出锁的控制权,等t2运行拿到objectLock的锁对象,在调用objectLock的notify()方法唤醒t1,也就是说t1和t2的等待和唤醒是有先后顺序的。那如果我们先调用t2的唤醒,在调用t1的等待,会有什么结果?结果就是t1永远不会被唤醒。

Condition的await()方法和signal()方法

   public static void main(String[] args) {

        final Lock lock = new ReentrantLock();
        final Condition condition = lock.newCondition();

        new Thread(() -> {
            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + ",come in");
                condition.await();
                System.out.println(Thread.currentThread().getName() + ",被唤醒");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }, "t1").start();

        //暂停几秒
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            lock.lock();
            try {
                condition.signal();
                System.out.println(Thread.currentThread().getName() + ",发出通知");
            } finally {
                lock.unlock();
            }
        }, "t2").start();
    }

        打印结果:

t1,come in
t2,发出通知
t1,被唤醒

        案例中,t1先获得lock对象,然后在调用lock对象中的Condition的await()方法让出lock对象的控制权。t2拿到lock的控制控制权之后,然后调用lock对象的Condition的signal()方法唤醒t1,完成了对t1线程的等待和唤醒流程。如果这里我们和Object 的wait()方法和notify()案例中一样先让t2调用condition.signal()唤醒方法,在让t1调用condition.await()方法进行等待,t1是否能正常的被唤醒呢?答案是t1将不能被唤醒,t1将永远等待下去。

 LockSupport的park()方法和unpark()方法

 public static void main(String[] args) {

        Thread t1 = new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + ",come in");
            LockSupport.park();
            System.out.println(Thread.currentThread().getName() + ",被唤醒");
        }, "t1");
        t1.start();

        //等待几秒
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            LockSupport.unpark(t1);
            System.out.println(Thread.currentThread().getName() + ",发送通知");
        }, "t2").start();
    }

        打印结果:

t1,come in
t2,发送通知
t1,被唤醒

LockSupport是Java中的一个工具类,用于实现线程的阻塞和唤醒操作。它提供了以下几个方法:

  1. park()方法:

    • 当一个线程调用LockSupport.park()时,它会被阻塞,进入等待状态。
    • park()方法不需要依赖任何对象的锁,因此可以在任意地方使用。
  2. unpark(Thread thread)方法:

    • 当一个线程调用LockSupport.unpark(thread)时,它会将指定的线程解除阻塞,使其从等待状态转变为可运行状态。
    • 如果指定的线程之前没有被阻塞,则下一次调用该线程的park()方法时,它仍然会被阻塞。

LockSupport与传统的等待/唤醒机制相比有以下几个特点:

  • LockSupport是基于线程而不是对象的,它不需要依赖于某个对象的锁来进行阻塞和唤醒操作。
  • 调用park()方法后,线程不需要被其他线程唤醒就可以自动解除阻塞。
  • unpark(thread)方法可以先于park()方法调用,这样即使线程还没有被阻塞也能够保证下一次调用park()时不会被阻塞。

形象的理解:

        线程阻塞需要消耗凭证(permit),这个凭证最多只有一个。当调用park方法时,如果有凭证则会直接消耗掉这个凭证然后正常退出,如果没有凭证,就必须阻塞等待凭证可用。unpark则相反,它会增加一个凭证,但最多只能有一个凭证,累加无效。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值