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 Methods Condition
前置条件 获取对象的锁 调用Lock.lock()获取锁
调用Lock.newCodition()获取Condition对象
调用方法 直接调用
如:Object.wait()
直接调用
如:condition.await()
等待队列个数 一个 多个
当前线程释放锁进入等待状态 支持 支持
当前线程释放锁进入等待状态,在等待状态中不响应中断 不支持 支持
当前线程释放锁并进入超时等待状态 支持 支持
当前线程释放锁并进入等待状态到将来某个时间 不支持 支持
唤醒等待队列中的一个线程 支持 支持
唤醒等待队列中的全部线程 支持 支持

你可能感兴趣的:(Java,队列,多线程,java,并发编程)