关于Java多线程中的wait()、sleep()、notify()和notifyAll()的简单介绍和使用

Java多线程的开启

使用Thread开启多线程

Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程开启");
            }
        });
        t.start();

wait()和sleep()的对比

wait()和sleep()都是能让线程进入休眠状态。准确的说:
1. 使用了wait方法的话,线程会进入一个等待状态,此刻,线程持有的monitor释放,同时等待获取到monitor才可以进行运行该线程。
2. 使用了sleep方法的话,线程进入一个休眠状态,使用该方法一般需要传入一个时间参数mills(毫秒),此刻同时,线程将休眠mills的时长,然后自动唤醒。

最主要的区别如下:

1. wait是Object中的方法,使用了wait方法,必须使用Object中的notify或notifyAll来唤醒线程。
2. sleep是Thread中的方法,使用了sleep方法,不需要其他操作,线程睡眠完就会自动唤醒运行。

wait方法

源码:

    /**
     * Causes the current thread to wait until another thread invokes the
     * {@link java.lang.Object#notify()} method or the
     * {@link java.lang.Object#notifyAll()} method for this object.
     * In other words, this method behaves exactly as if it simply
     * performs the call {@code wait(0)}.
     * <p>
     * The current thread must own this object's monitor. The thread
     * releases ownership of this monitor and waits until another thread
     * notifies threads waiting on this object's monitor to wake up
     * either through a call to the {@code notify} method or the
     * {@code notifyAll} method. The thread then waits until it can
     * re-obtain ownership of the monitor and resumes execution.
     * <p>
     * As in the one argument version, interrupts and spurious wakeups are
     * possible, and this method should always be used in a loop:
     * <pre>
     *     synchronized (obj) {
     *         while (&lt;condition does not hold&gt;)
     *             obj.wait();
     *         ... // Perform action appropriate to condition
     *     }
     * </pre>
     * This method should only be called by a thread that is the owner
     * of this object's monitor. See the {@code notify} method for a
     * description of the ways in which a thread can become the owner of
     * a monitor.
     *
     * @throws  IllegalMonitorStateException  if the current thread is not
     *               the owner of the object's monitor.
     * @throws  InterruptedException if any thread interrupted the
     *             current thread before or while the current thread
     *             was waiting for a notification.  The <i>interrupted
     *             status</i> of the current thread is cleared when
     *             this exception is thrown.
     * @see        java.lang.Object#notify()
     * @see        java.lang.Object#notifyAll()
     */
    public final void wait() throws InterruptedException {
        wait(0);
    }
从源码中可以看出

1. 首先,使用wait,可以使得线程进入一个等待状态。
2. 其次,每个线程必须持有该对象的monitor。如果在当前线程中调用wait()方法之后,该线程就会释放monitor的持有对象并让自己处于等待状态。
3. 调用wait方法必须使用同步锁synchronized的代码快中.
4. 如果想唤醒该线程,必须在另一个线程中,并在synchronized代码块中使用notify或者notifyAll才能重新让线程获取到monitor,线程才能被唤醒。(一定要在另一个线程中才有效)

测试代码和结果
1. 正确的使用wait和notify例子,ThreadTest为本类

    public static void main(String[] strings){
        ThreadTest test = new ThreadTest();
        test.waitThread();
        Scanner scanner = new Scanner(System.in);
        System.out.println("任意输入,唤醒线程:");
        scanner.nextLine();
        test.notifyWaitThread();
    }

    /**
     * 对象锁
     */
    private final Object object = new Object();

    /**
     *
     * 使用Object.wait()方法使得线程进入等待状态,使得该线程释放了该线程拥有的monitor
     * 使用wait方法,必须使用Object的notify和notifyAll来让线程重新获取到monitor对象,唤醒线程
     *
     */
    private void waitThread(){
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程1开启");
                synchronized (object){
                    System.out.println("线程1进入等待状态");
                    try {
                        object.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("线程1结束");

            }
        });
        t.start();
   }

 private void notifyWaitThread(){
        synchronized(object){
            System.out.println("开始唤醒等待中的线程");
            object.notify();
        }
    }
运行的结果:

可以看出,线程被成功唤醒,其中唤醒等待线程的线程为main主线程
这里写图片描述

2. 在使用wait的线程中进行notify操作的例子,ThreadTest为本类
 private void waitThread(){
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程1开启");
                synchronized (object){
                    System.out.println("线程1进入等待状态");
                    try {
                        object.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //使用notify唤醒线程
                    object.notify();
                }
                System.out.println("线程1结束");

            }
        });
        t.start();
 }
 private void waitThread(){
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程1开启");
                synchronized (object){
                    System.out.println("线程1进入等待状态");
                    try {
                        object.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //使用notify唤醒线程
                synchronized (object){
                    object.notify();
                }
                System.out.println("线程1结束");
            }
        });
        t.start();
}

main方法中进行了修改

public static void main(String[] strings){
        ThreadTest test = new ThreadTest();
        test.waitThread();
 }
运行结果都是线程没有获取到monitor才处于等待的状态

这里写图片描述

sleep方法

相信很多人都用过该方法,这里直接进行简单的演示

代码
    /**
     * sleep是Thread的方法,可使得当前线程进入休眠状态,但是和Object的wait方法的区别在于,使用sleep方法线程没有释放对应的monitor对象,不需要通过notify来唤醒,时间走完就恢复
     * @param millis
     */
    private void sleepThread(long millis){
        Thread thread = new Thread(()->{
           System.out.println("sleepThread创建");
           System.out.println("sleepThread进入睡眠状态,时长为:" + millis);
            try {
                Thread.sleep(millis);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("sleepThread睡眠结束,被唤醒了");
        });
        thread.start();
    }

main函数中

public static void main(String[] strings){
        ThreadTest test = new ThreadTest();
        test.sleepThread(2000);
}
运行结果

这里写图片描述

下面简单说一下notify和notifyAll

notify

源码
    /**
     * Wakes up a single thread that is waiting on this object's
     * monitor. If any threads are waiting on this object, one of them
     * is chosen to be awakened. The choice is arbitrary and occurs at
     * the discretion of the implementation. A thread waits on an object's
     * monitor by calling one of the {@code wait} methods.
     * <p>
     * The awakened thread will not be able to proceed until the current
     * thread relinquishes the lock on this object. The awakened thread will
     * compete in the usual manner with any other threads that might be
     * actively competing to synchronize on this object; for example, the
     * awakened thread enjoys no reliable privilege or disadvantage in being
     * the next thread to lock this object.
     * <p>
     * This method should only be called by a thread that is the owner
     * of this object's monitor. A thread becomes the owner of the
     * object's monitor in one of three ways:
     * <ul>
     * <li>By executing a synchronized instance method of that object.
     * <li>By executing the body of a {@code synchronized} statement
     *     that synchronizes on the object.
     * <li>For objects of type {@code Class,} by executing a
     *     synchronized static method of that class.
     * </ul>
     * <p>
     * Only one thread at a time can own an object's monitor.
     *
     * @throws  IllegalMonitorStateException  if the current thread is not
     *               the owner of this object's monitor.
     * @see        java.lang.Object#notifyAll()
     * @see        java.lang.Object#wait()
     */
    public final native void notify();
从源码可以看出

1. notify可以唤醒该monitor的第一个wait的线程,而且只唤醒一个线程
2. notify必须在另外线程中使用,并且得在synchronized代码块中执行,并且对应的锁应该是该线程的monitor持有的对象

例子:多个线程进入等待状态,使用一次notify只能唤醒第一个进入等待的线程
    private class MyRunnable implements Runnable{

        private String name;
        public MyRunnable(String name){
            this.name = name + "线程";
        }

        @Override
        public void run() {
            System.out.println(name + "启动");
            synchronized (object){
                try {
                    System.out.println(name + "进入wait状态");
                    object.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(name + "被唤醒并执行结束");
        }
    }

    private void startAndWaitAllThread(){
        String names[] = {"A", "B", "C", "D", "E"};
        for (int i = 0; i < names.length; i ++){
            Thread thread = new Thread(new MyRunnable(names[i]));
            thread.start();
        }
    }

    public static void main(String[] strings) throws InterruptedException {
        ThreadTest test = new ThreadTest();
        test.startAndWaitAllThread();
        Thread.sleep(2000);
        test.notifyWaitThread();
    }

执行结果:

这里写图片描述

notifyAll

源码
    /**
     * Wakes up all threads that are waiting on this object's monitor. A
     * thread waits on an object's monitor by calling one of the
     * {@code wait} methods.
     * <p>
     * The awakened threads will not be able to proceed until the current
     * thread relinquishes the lock on this object. The awakened threads
     * will compete in the usual manner with any other threads that might
     * be actively competing to synchronize on this object; for example,
     * the awakened threads enjoy no reliable privilege or disadvantage in
     * being the next thread to lock this object.
     * <p>
     * This method should only be called by a thread that is the owner
     * of this object's monitor. See the {@code notify} method for a
     * description of the ways in which a thread can become the owner of
     * a monitor.
     *
     * @throws  IllegalMonitorStateException  if the current thread is not
     *               the owner of this object's monitor.
     * @see        java.lang.Object#notify()
     * @see        java.lang.Object#wait()
     */
    public final native void notifyAll();
使用notifyAll的区别就是可以使所有的等待线程获取到monitor,并唤醒,但是顺序是随机的,下面看一下执行的效果

在main中使用notifyAll唤醒线程

   public static void main(String[] strings) throws InterruptedException {
        ThreadTest test = new ThreadTest();
        test.startAndWaitAllThread();
        Thread.sleep(2000);
        test.notifyAllWaitThread();
   }

执行效果
这里写图片描述

可以看出,唤醒的顺序是随机的

到这里就结束结束啦,欢迎提意见留意

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值