Java 并发编程wait、notify、notifyAll 与 Condition

一、wait、notify、notifyAll

1.1 方法简介

1)调用某个对象的wait()方法,相当于让当前线程交出此对象的锁,然后进入等待状态,等待后续再次获得此对象的锁(Thread类中的sleep方法使当前线程暂停执行一段时间,从而让其他线程有机会继续执行,但它并不释放对象锁)。如果调用某个对象的wait()方法,当前线程必须拥有这个对象的锁,因此调用wait()方法必须在同步块或者同步方法中进行(synchronized块或者synchronized方法)。

2)notify()方法能够唤醒一个正在等待该对象的锁的线程,当有多个线程都在等待该对象的锁的话,则只能唤醒其中一个线程,具体唤醒哪个线程则不得而知。调用某个对象的notify()方法,当前线程也必须拥有这个对象的锁,因此调用notify()方法必须在同步块或者同步方法中进行(synchronized块或者synchronized方法)。

3)notifyAll()方法能够唤醒全部正在等待该对象的锁的线程,最终谁能拿到锁不得而知。用某个对象的notifyAll()方法,当前线程也必须拥有这个对象的锁,因此调用notifyAll()方法必须在同步块或者同步方法中进行(synchronized块或者synchronized方法)。

注意:一个线程被唤醒不代表立即获取了对象的锁,只有等调用完notify()或者notifyAll()并退出synchronized块,释放对象锁后,其余线程才可获得锁执行。

1.2 相关方法
方法名称描述
wait()调用该方法的线程进入WAITING状态,只有等待另外线程的通知或被中断才会返回,需要注意,调用wait()方法后,会释放对象的锁。
wait(long)超时等待一段时间,这里的参数是毫秒,也就是等待长达n毫秒,如果没有通知就超时返回。
wait(long, int)对于超时时间更细粒度的控制,可以达到毫秒。
notify()通知一个在对象上等待的线程,使其从wait()返回,而返回的前提是该线程获取到了对象的锁。
notifyAll()通知所有等待在该对象上的线程。

1.3 示例

public class Test {

    Object lock = new Object();

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

        Runnable runnable1 = new Runnable() {
            @Override
            public void run() {
                test.await(); // 等待被唤醒
            }
        };

        Runnable runnable2 = new Runnable() {
            @Override
            public void run() {
                test.anotify(); // 唤醒工作线程
            }
        };

        Thread thread1 = new Thread(runnable1);
        Thread thread2 = new Thread(runnable2);
        thread1.start();
        thread2.start();
    }

    public void await() {
        synchronized (lock) {
            try {
                System.out.println(Thread.currentThread().getName() + ": 等待被唤醒");
                lock.wait();
                System.out.println("开始工作...");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }

    public void anotify() {
        synchronized (lock) {
            try {
                System.out.println("10秒后唤醒工作线程...");
                TimeUnit.SECONDS.sleep(10);
                lock.notify();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}

二、Condition

2.1 简介

任何一个java对象都天然继承于Object类,在线程间实现通信的往往会应用到Object的几个方法,比如wait(),wait(long timeout),wait(long timeout, int nanos)与notify(),notifyAll()几个方法实现等待/通知机制,同样的, 在java Lock体系下依然会有同样的方法实现等待/通知机制。从整体上来看Object的wait和notify/notify是与对象监视器配合完成线程间的等待/通知机制,而Condition与Lock配合完成等待通知机制,前者是java底层级别的,后者是语言级别的,具有更高的可控制性和扩展性。两者除了在使用方式上不同外,在功能特性上还是有很多的不同:

  • Condition能够支持不响应中断,而通过使用Object方式不支持;
  • Condition能够支持多个等待队列(new 多个Condition对象),而Object方式只能支持一个;
  • Condition能够支持超时时间的设置,而Object不支持
2.2 先关方法
方法描述
await()造成当前线程在接到信号或被中断之前一直处于等待状态。
await(long time, TimeUnit unit)造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。
awaitNanos(long nanosTimeout)造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。返回值表示剩余时间,如果在nanosTimesout之前唤醒,那么返回值 = nanosTimeout - 消耗时间,如果返回值 <= 0 ,则可以认定它已经超时了。
awaitUninterruptibly()造成当前线程在接到信号之前一直处于等待状态。【注意:该方法对中断不敏感】。
awaitUntil(Date deadline)造成当前线程在接到信号、被中断或到达指定最后期限之前一直处于等待状态。如果没有到指定时间就被通知,则返回true,否则表示到了指定时间,返回返回false。
signal()唤醒一个等待线程。该线程从等待方法返回前必须获得与Condition相关的锁。
signalAll()唤醒所有等待线程。能够从等待方法返回的线程必须获得与Condition相关的锁。
2.3 示例
public class Test {
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();

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

        Runnable runnable1 = new Runnable() {
            @Override
            public void run() {
                test.await(); // 等待被唤醒
            }
        };

        Runnable runnable2 = new Runnable() {
            @Override
            public void run() {
                test.anotify(); // 唤醒工作线程
            }
        };

        Thread thread1 = new Thread(runnable1);
        Thread thread2 = new Thread(runnable2);
        thread1.start();
        thread2.start();
    }

    public void await() {
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + ": 等待被唤醒");

            condition.await();

            System.out.println("开始工作...");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void anotify() {
        lock.lock();
        try {
            System.out.println("10秒后唤醒工作线程...");

            TimeUnit.SECONDS.sleep(10);

            condition.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

}

三、两者比较

对比项Object Monitor MethodsCondition
前置条件获取对象的锁调用Lock.lock()获取锁
调用Lock.newCodition()获取Condition对象
调用方法直接调用
如:Object.wait()
直接调用
如:condition.await()
等待队列个数一个多个
当前线程释放锁进入等待状态支持支持
当前线程释放锁进入等待状态,在等待状态中不响应中断不支持支持
当前线程释放锁并进入超时等待状态支持支持
当前线程释放锁并进入等待状态到将来某个时间不支持支持
唤醒等待队列中的一个线程支持支持
唤醒等待队列中的全部线程支持支持
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

书香水墨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值