多线程协作——wait、notify方法

本文详细解析了Java中线程同步的关键方法wait(), notify() 和 notifyAll() 的使用方式及注意事项。通过HandlerThread源码示例展示了如何正确地使用这些方法来实现线程间的同步。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

wait()方法

1.Causes the current thread to wait until another thread invokes the java.lang.Object.notify() method or the java.lang.Object.notifyAll() method for this object. In other words, this method behaves exactly as if it simply performs the call wait(0).这段在javadoc的描述介绍了wait()方法的作用,wait()是Object类的方法,作用是停止当前线程(执行wait()方法的线程)直到另一个线程调用了这个Object类的notify()或者notifyAll()方法

2.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 notify method or the notifyAll method. The thread then waits until it can re-obtain ownership of the monitor and resumes execution. (这是doc的第二段,百度翻译:当前线程必须拥有该对象的监视器。线程释放该监视器的所有权,并等待直到另一线程通知该对象监视器上等待的线程通过调用通知方法或NoTIFYALL方法唤醒。然后线程等待,直到它能够重新获得监视器的所有权并恢复执行。)百度翻译的还是能让人理解的,这段话中唯一一个有争议的翻译估计就是monitor,后面我也就按照“监视器”翻译了,我查了一下,在Java字节码层面,获取锁的字节码指令为:monitorenter,释放锁的字节码指令为:monitorexit。

3.As in the one argument version, interrupts and spurious wakeups are possible, and this method should always be used in a loop: 

第三句注释是说在wait过程中,是会出现打断和虚假唤醒的情况,所以我们平常都是需要在循环中使用的,而不是if判断,例如:

     synchronized (obj) {
         while (<condition does not hold>)
             obj.wait();
         ... // Perform action appropriate to condition
     }

我前篇文章介绍的HandlerThread就有一个地方用到了wait(),很好的一个使用例子,下面就以这段代码简单介绍一下以上三点:

 HandlerThread looperThread=new HandlerThread("LooperMusic");
		looperThread.start();
		handler=new Handler(looperThread.getLooper());

想要用HandlerThread下的Handler,就需要指定其Looper。如果HandlerThread没有调用start()方法,这时候会报该线程dead的错误;但即使先调用的start()方法,也不一定可以得到Looper的实例,为什么呢?了解Handler机制的同学都知道,Looper的初始工作是在run()方法里执行的,而run()方法是有cpu调度的,所以并非立即就存在的。所以getLooper()是如何保证Looper不为空的呢,没错通过wait()。请欣赏源码:

   /**
     * This method returns the Looper associated with this thread. If this thread not been started
     * or for any reason is isAlive() returns false, this method will return null. If this thread 
     * has been started, this method will block until the looper has been initialized.  
     * @return The looper.
     */
    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
        
        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();//在这里阻塞调用改方法的线程
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

整个同步代码块就是第2点的所阐述的内容,这里的Object则是handlerThread这个实例。对象监视器的最直观体现则是这个锁,调用wait()后,线程在handlerThread的监视器中等待,并且会释放这个对象锁。直到有其它线程调用handlerThread的notify()

很清晰的代码,线程如果不在存活直接返回null;

线程存活而mLooper为空的话,则等待直到looper被创建。(第3点

所以很容易就联想到这部分是在looper创建后被唤醒的;那么去看run()方法,果然:

 @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();//在这里唤醒
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }

异常:

IllegalMonitorStateException - if the current thread is not the owner of the object's monitor.

这个异常就是针对第2点的问题,如果你当前线程没有拿到相应对象的锁就直接调用wait()就会抛出该异常

InterruptedException - if any thread interrupted the current thread before or while the current thread was waiting for a notification. The interrupted status of the current thread is cleared when this exception is thrown.

 

notify()、notifyAll()

以下是notify()的doc:

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 wait methods.

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.

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:

  • By executing a synchronized instance method of that object.
  • By executing the body of a synchronized statement that synchronizes on the object.
  • For objects of type Class, by executing a synchronized static method of that class.

Only one thread at a time can own an object's monitor.

就不再一一翻译了,我简单的说一下这三段话要表达的意思:

1.调起一个线程,一个。对于Object来说可能在它下面挂起了多个线程,而notify()只是唤醒其中一个,这句话解释了notify和notifyAll的区别

2.第二段话是说,调用了notify()后被唤醒的线程不会立即执行,它需要获得这个对象的锁,而且获取这个锁时并没有优势,和其它需要获取锁的过程是一样的

3.第三段主要表达了一种对应关系,继续拿上面HandlerThread举例,挂起(wait())用的是HandlerThread的实例,唤醒(notify())也要用HandlerThread的实例。然后就是举例说明三种同步的方式:同步方法,同步代码块,同步静态方法。

HandlerThread中则使用同步代码块的方式

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值