Java并发之wait方法

wait()

Object的wait方法调用使当前线程阻塞,并且释放锁等待,直到其他线程调用notify或者notifyAll将其唤醒,唤醒之后获取锁继续执行,需要与synchronized一起使用。通常用于当前线程等待满足一定条件之后才能运行。wait方法javadoc原文:

Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object. In other words, this method behaves exactly as if it simply performs the call wait(0).
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.
As in the one argument version, interrupts and spurious wakeups are possible, and this method should always be used in a loop:
           synchronized (obj) {
               while (<condition does not hold>)
                   obj.wait();
               ... // Perform action appropriate to condition
           }
       
This method should only be called by a thread that is the owner of this object's monitor. See the 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.
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.

对javadoc的解释:

  1. the current thread to wait ,当前线程等待
  2. The current thread must own this object's monitor,当前线程必须持有某个对象的同步锁
  3. The thread releases ownership of this monitor,当前线程会释放它所持有的同步锁
  4. until another thread invokes the notify() method or the notifyAll() method for this object,其他线程调用同一个同步锁的对象的notify()/notifyAll()方法,这个同步锁的对象是和wait()线程是同一个对象,同一个锁
  5. The thread then waits until it can re-obtain ownership of the monitor and resumes execution,这个线程被唤醒之后,会等到重新获取同步锁之后才会继续执行

异常:

  1. IllegalMonitorStateException,当前线程没持有对象的同步锁
  2. InterruptedException,并其他线程打断,在wait之前或者当中打断都会抛错,打断应该是设置个interrupt标记

几个注意点:

  1. 必须先持有某个对象的同步锁,这个对象就是方法的调用者
  2. 是当前线程被等待
  3. wait之后,释放锁
  4. 唤醒之后,必须先获取同一个对象的同步锁才能继续

这个对象怎么确定?是哪个对象的同步锁?

当前线程需要获取wait()方法调用者对象的同步锁,比如:t1.wait(),需要持有t1的同步锁,obj.wait()需要持有obj的同步锁。

获取锁    --------->    wait()    --------->    释放锁    --------->    阻塞状态    --------->    被notify    --------->    就绪状态    --------->    获取锁    --------->    运行

示例

1、当前线程没有持有锁,报错IllegalMonitorStateException

try {
    wait();
} catch (InterruptedException e) {
    e.printStackTrace();
}

2、当前线程持有的锁的对象和wait()方法调用的对象不是同一个,报错IllegalMonitorStateException

synchronized (this) {
    try {
        Object obj = new Object();
        obj.wait();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

3、当前线程持有的锁的对象和方法调用者是同一个,不报错

synchronized (this) {
    try {
        wait();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

 notify()/notifyAll()

唤醒对象同步锁上的等待线程,分别是唤醒一个和所有。

异常:

  • IllegalMonitorStateException,当前线程没持有对象的同步锁

几个注意点:

  • 必须先持有某个对象的同步锁,这个对象就是方法的调用者
  • 唤醒的是方法调用者对象的同步锁上的等待线程,不会释放锁

这个对象怎么确定?是哪个对象的同步锁?

当前线程需要获取notify()方法调用者对象的同步锁,比如:t1.notify(),需要持有t1的同步锁,obj.notify()需要持有obj的同步锁。

示例

1、当前线程没有持有锁,报错IllegalMonitorStateException

private void test2() {
    notify();
}

2、当前线程持有的锁的对象和notify()方法调用的对象不是同一个,报错IllegalMonitorStateException

private void test3() {
    Object obj = new Object();
    synchronized (this) {
        obj.notify();
    }
}

3、当前线程持有的锁的对象和方法调用者是同一个,不报错

private void test1() {
    synchronized (this) {
        notify();
    }
}

 

wait()和notify()

调用wait方法和调用notify方法,需要使用同一个对象的同步锁才能正常唤醒线程,wait方法被唤醒之后,需要获取到锁才能继续执行,因此在notify调用时候,包装notify之后不要有阻塞的代码出现,必须保证锁能够正确的被释放,否则wait的线程就不能真正的被唤醒,因为wait的线程不能获取到锁,也就不能继续执行。

示例

1、使用第三方对象作为锁

/**
 * 第三方对象作为锁
 */
private void test1() {
    Object lock = new Object();
    ThreadA t1 = new ThreadA(lock);
    Thread t = new Thread(t1);
    t.setName("thread-a");

    synchronized (lock) {
        t.start();
        try {
            Thread.sleep(2000);
            System.out.println(Thread.currentThread().getName() + " wait ");
            lock.wait();
            System.out.println(Thread.currentThread().getName() + " continue ");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}


class ThreadA implements Runnable {

    private Object lock;

    public ThreadA(Object lock) {
        this.lock = lock;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " start ");
        // 需要获取obj的锁才能运行
        synchronized (lock) {
            System.out.println(Thread.currentThread().getName() + " call notify");
            lock.notify();
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

lock.notify()之后5秒,main线程才继续执行

2、使用wait方法调用者的锁

/**
 * 使用wait方法调用者的锁
 */
private  void test2() {
    ThreadB t1 = new ThreadB(this);
    Thread t = new Thread(t1);
    t.setName("thread-b");

    synchronized (this) {
        t.start();
        try {
            Thread.sleep(5000);
            System.out.println(Thread.currentThread().getName() + " wait ");
            wait();
            System.out.println(Thread.currentThread().getName() + " continue ");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}


class ThreadB implements Runnable {
    private Object lock;

    public ThreadB(Object lock) {
        this.lock = lock;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " start ");
        // 需要获取obj的锁才能运行
        synchronized (lock) {
            System.out.println(Thread.currentThread().getName() + " call notify");
            lock.notify();
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

lock.notify()之后5秒,main线程才继续执行

 3、使用notify方法调用者的锁

/**
 * 使用notify方法调用者的锁
 */
private void test3() {
    ThreadC t1 = new ThreadC();
    Thread t = new Thread(t1);
    t.setName("thread-c");

    synchronized (t1) {
        t.start();
        try {
            System.out.println(Thread.currentThread().getName() + " wait ");
            t1.wait();
            System.out.println(Thread.currentThread().getName() + " continue ");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class ThreadC implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " start ");
        // 需要获取obj的锁才能运行
        synchronized (this) {
            System.out.println(Thread.currentThread().getName() + " call notify");
            notify();
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

如果使用Thread对象作为锁的话,不用notify,main现在也可以被唤醒,不太清楚为什么?最后还是不要用Thread对象作为锁。

join()的javadoc描述的As a thread terminates the this.notifyAll method is invoked. It is recommended that applications not use wait, notify, or notifyAll on Thread instances.

一个线程销毁之后,会调用notifyAll方法,应该是JVM底层c++实现的,因此不建议使用Thread对象的同步锁来调用wait,notify,notifyAll

最后,wait方法的注释说,为了避免wait被意外中断或者被意外唤醒,建议这么使用

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

wait()和sleep()区别

wait方法使当前线程从运行状态到等待阻塞状态,并且释放当前同步锁,sleep方法使当前线程从运行状态到休眠阻塞状态,不会释放锁。

join()

之前说过,使用Thread对象的同步锁时候,Thread对象销毁之后,JVM底层会notifyAll。join方法就是如此,查看源码

public final synchronized void join(long millis)
    throws InterruptedException {
    long base = System.currentTimeMillis();
    long now = 0;

    if (millis < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }

    if (millis == 0) {
        while (isAlive()) {
            wait(0);
        }
    } else {
        while (isAlive()) {
            long delay = millis - now;
            if (delay <= 0) {
                break;
            }
            wait(delay);
            now = System.currentTimeMillis() - base;
        }
    }
}

我们看到只调用了wait方法,唤醒有底层实现。join方法是Thread类的实例方法,一个Thread实现调用这个方法,表示将当前线程等待阻塞,直到这个Thread被死亡为止。

 

转载于:https://my.oschina.net/cregu/blog/2250243

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值