欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

线程通知和等待

程序员文章站 2022-06-23 11:16:03
public class WaitNotifyTest { /** * 资源A */ private static volatile Object resourceA = new Object(); /** * 资源B */ private static volatile Object resourceB = new Object(); public static void main(String[] args) throw...

Java 中的 Object 类是所有类的父类,鉴于继承机制, Java 把所有类都需要的方法放Object 类里面,下面看下Object 类中和线程相关的方法:

public class Object {

	public final native void notify();

    public final native void notifyAll();

    public final native void wait(long timeout) throws InterruptedException;

    public final void wait(long timeout, int nanos) throws InterruptedException {
        if (timeout < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException(
                                "nanosecond timeout value out of range");
        }

        if (nanos > 0) {
            timeout++;
        }

        wait(timeout);
    }

    public final void wait() throws InterruptedException {
        wait(0);
    }
}

上面Object中线程的方法都需要在获取到对象的监视器锁中使用,否者线程会抛出 IllegalMonitorStateException 异常。

先简单说一下监视器锁,后面会有博文详细说明:

synchronized

执行 synchronized 步代码块时 使用该共享变量作为参数。

synchronized (共享变量)//doSomething 
}

调用该共享变量的方法,并且该方法使用了 synchronized 修饰

synchronized void add (int a , int b) { 
	//doSomething
}

wait

当一个线程调用一个共享变量 wait()方法时, 该调用线程会被阻塞挂起, 到发生下面几件事情才返回

  • 其他线程调用了该共享对象 notify()或者 notifyAll()方法;
  • 其他线程调用了该线程 interrupt()方法,导致该线程抛出 InterruptedException 异常返回。

虚假唤醒

一个线程可以从挂起状态变为可以运行状态(就是被唤醒),即使该线程没有被其他线程调用 notify()、 notifyAll()方法进行通知,或者被中断,或者等待超时,这就是所谓的虚假唤醒。

要怎么避免虚假唤醒呢?

synchronized (obj) {
	while (条件不满足) {
		obj.wait();
	}
}

线程等待不释放锁

继续来看一个例子:
当前线程调用共享变量的 wait()方法后只会释放当前共享变量上的锁,如果当前线程还持有其他共享变量的锁,则这些锁是不会被释放的。

public class WaitNotifyTest {

    /**
     * 资源A
     */
    private static volatile Object resourceA = new Object();
    /**
     * 资源B
     */
    private static volatile Object resourceB = new Object();

    public static void main(String[] args) throws InterruptedException {
        Thread threadA = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    synchronized (resourceA) {
                        // 获取resourceA共享资源的监视器锁
                        System.out.println("threadA 获取到 resourceA 监视器锁");

                        // 获取resourceB共享资源的监视器锁
                        synchronized (resourceB) {
                            System.out.println("threadA 获取到 resourceB 监视器锁");

                            // 线程A阻塞, 并释放获取到的resourceA锁
                            System.out.println("threadA 释放 resourceA 监视器锁");
                            resourceA.wait();
                        }
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        Thread threadB = new Thread(new Runnable() {
            @Override
            public void run() {

                try {
                    // 睡眠1s
                    Thread.sleep(1000);

                    synchronized (resourceA) {
                        // 获取resourceA共享资源的监视器锁
                        System.out.println("threadB 获取到 resourceA 监视器锁");

                        System.out.println("threadB 尝试获取 resourceB 监视器锁...");
                        // 获取resourceB共享资源的监视器锁
                        synchronized (resourceB) {
                            System.out.println("threadB 获取到 resourceB 监视器锁");

                            // 线程B阻塞, 并释放获取到的resourceA锁
                            System.out.println("threadB 释放 resourceA 监视器锁");
                            resourceA.wait();
                        }
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        threadA.start();
        threadB.start();

        threadA.join();
        threadB.join();

        System.out.println("main over");
    }
}
threadA 获取到 resourceA 监视器锁
threadA 获取到 resourceB 监视器锁
threadA 释放 resourceA 监视器锁
threadB 获取到 resourceA 监视器锁
threadB 尝试获取 resourceB 监视器锁...

由上面代码测试结果可以看出: 当前线程调用共享对象的wait()方法时, 当前线程只会释放当前共享对象的锁, 当前线程持有的其他共享对象的监视器锁不会被释放掉!

中断等待中的线程

当一个线程调用共享对象的wait() 方法被阻塞挂起后, 如果其他线程中断了该线程, 则会抛出InterruptedException异常并返回

public class WaitNotifyInterrupt {

    static Object obj = new Object();

    public static void main(String[] args) throws InterruptedException {
        Thread threadA = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("----begin----");

                    synchronized (obj) {
                        obj.wait();
                    }
                    System.out.println("----end----");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        threadA.start();

        Thread.sleep(1000);

        System.out.println("----begin interrupt threadA----");
        threadA.interrupt();
        System.out.println("----end interrupt threadA----");
    }
}
----begin----
----begin interrupt threadA----
----end interrupt threadA----
java.lang.InterruptedException
	at java.lang.Object.wait(Native Method)
	at java.lang.Object.wait(Object.java:502)
	at base.thread.book.WaitNotifyInterrupt$1.run(WaitNotifyInterrupt.java:19)
	at java.lang.Thread.run(Thread.java:748)

notify

一个线程调用共享对象的 notify()方法后,会唤醒一个在该共享变量上调用wait系列方法后被挂起的线程。一个共享变量上可能会有多个线程在等待,具体唤醒哪个等待的线程是随机的。

此外,被唤醒的线程不能马上从 wait 方法返回并继续执行,它必须在获取了共享对象的监视器锁后才可以返回,也就是唤醒它的线程释放了共享变量上的监视器锁后,被唤醒的线程也不一定会获取到共享对象的监视器锁,这是因为该线程还需要和其他线程一起竞争该锁,只有该线程竞争到了共享变量的监视器锁后才可以继续执行。

/**
 * 资源A
 */
private static volatile Object resourceA = new Object();

public static void main(String[] args) throws InterruptedException {
    Thread threadA = new Thread(new Runnable() {
        @Override
        public void run() {
            // 获取resourceA共享资源的监视器锁
            synchronized (resourceA) {

                System.out.println("threadA get resourceA lock");

                try {
                    System.out.println("threadA start wait");
                    resourceA.wait();
                    System.out.println("threadA end wait");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    });

    Thread threadB = new Thread(new Runnable() {
        @Override
        public void run() {
            synchronized (resourceA) {
                System.out.println("threadB get resourceA lock");

                try {
                    System.out.println("threadB start wait");
                    resourceA.wait();
                    System.out.println("threadB end wait");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    });

    Thread threadC = new Thread(new Runnable() {
        @Override
        public void run() {
            synchronized (resourceA) {
                System.out.println("threadC begin notify");
                resourceA.notify();
            }
        }
    });

    threadA.start();
    threadB.start();

    Thread.sleep(1000);
    threadC.start();

    threadA.join();
    threadB.join();
    threadC.join();

    System.out.println("main over");
}
threadA get resourceA lock
threadA start wait
threadB get resourceA lock
threadB start wait
threadC begin notify
threadA end wait

从代码执行结果可以看出:threadB 线程还在等待中,threadA 线程已经执行结束了。

notifyAll

不同于在共享变量上调用 notify()函数会唤醒被阻塞到该共享变量上的一个线程, notifyAll()方法则会唤醒所有在该共享变量上由于调用 wait 系列方法而被挂起的线程。

/**
 * 资源A
 */
private static volatile Object resourceA = new Object();

public static void main(String[] args) throws InterruptedException {
    Thread threadA = new Thread(new Runnable() {
        @Override
        public void run() {
            // 获取resourceA共享资源的监视器锁
            synchronized (resourceA) {

                System.out.println("threadA get resourceA lock");

                try {
                    System.out.println("threadA start wait");
                    resourceA.wait();
                    System.out.println("threadA end wait");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    });

    Thread threadB = new Thread(new Runnable() {
        @Override
        public void run() {
            synchronized (resourceA) {
                System.out.println("threadB get resourceA lock");

                try {
                    System.out.println("threadB start wait");
                    resourceA.wait();
                    System.out.println("threadB end wait");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    });

    Thread threadC = new Thread(new Runnable() {
        @Override
        public void run() {
            synchronized (resourceA) {
                System.out.println("threadC begin notify");
                resourceA.notifyAll();
            }
        }
    });

    threadA.start();
    threadB.start();

    Thread.sleep(1000);
    threadC.start();

    threadA.join();
    threadB.join();
    threadC.join();

    System.out.println("main over");
}
threadA get resourceA lock
threadA start wait
threadB get resourceA lock
threadB start wait
threadC begin notify
threadB end wait
threadA end wait
main over

从代码执行结果看:threadA 和 threadB 都执行结束了。

本文地址:https://blog.csdn.net/weixin_45847167/article/details/112611672