Java 多线程解密之wait等待

  在Java中,可以调用wait()方法使当前线程进入object的等待队列,这样当有新的任务需要执行的时候,调用object的notify()或者notifyAll()方法就可以唤起线程,wait(),notify()在线程池、数据库连接池有有广泛的应用。

  其流程为:


  首先我们知道,在获取对象的锁,也就是获取到其监视器,获取锁成功之后,则线程进入 RUNNABLE状态,即运行状态,这时候如果调用其对象的wait()方法,即进入对象的等待队列,然后会将锁释放。这时候,别的线程可以获取到这个对象的锁,然后调用其 notifyAll() 方法,这样,所有在等待队列中的线程(即 WAITING 状态)都会进入这个线程的 同步队列,即锁队列。注意 ,调用  notifyAll() 方法 的线程此时可能还并没有释放锁。必须等这个线程释放锁之后,同步队列中的线程才可以一个一个的执行(同步队列中的线程获取到锁之后,必须执行完同步代码块,才会释放锁),也就是说,wait()之后进入到等待队列,notifyAll()后进入锁队列,但只有重新获取到锁之后才能从wait()方法返回,但当wait()的时候线程被打断呢?其实打断的时候,线程也是进入到了锁队列,跟上述一样,而只有当重新获取到锁之后,才会走catch(Execption)的代码块,而不是从wait()方法返回。
  有人可能要说了?为什么要这么设计?为什么notify()之后能不能马上从wait()方法返回?为什么还要等再次获取到锁的时候才能返回???
  因为只有这样,才能保证在多个线程都在执行Object的同步块的时候:

  synchronized (Object) {


  }


  同一时间,只有一个线程在 synchronized 代码块中!其它的线程要么在Object的等待队列中,要么就是在锁队列中!这样就避免了多个线程在同步块中执行导致的同步问题。

  

  下面举示例来说明:

一、wait()方法与notify()方法

  两个等待线程WaitRunnable,一个唤起线程 NotifyAllRunnable,两个等待线程WaitRunnable 等待一段时间之后,唤起线程 NotifyAllRunnable运行,然后调用 NotifyAll方法,然后等待一段时间才释放锁。两个等待线程WaitRunnable在wait()方法唤起之后,又会等待一段时间,那么,谁先运行完?谁再运行完呢?

public class Main {

    public static final Object waitObject = new Object();

    public static void main(String[] args) {

        Thread waitThread1 = new Thread(new WaitRunnable(), "waitThread1");
        Thread waitThread2 = new Thread(new WaitRunnable(), "waitThread2");

        waitThread1.start();
        waitThread2.start();

        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        Thread notifyAllThread = new Thread(new NotifyAllRunnable(), "notifyAllThread");
        notifyAllThread.start();
    }
}


WaitRunnable:

public class WaitRunnable implements Runnable{

    @Override
    public void run() {


        synchronized (Main.waitObject) {

            System.out.println(Thread.currentThread().getName() + " 开始wait ");
            try {
                /**
                 * 会释放锁
                 */
                Main.waitObject.wait();
                /**
                 * 又拿到锁
                 */
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " wait结束,又获取到锁 ");

            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " wait sleep 结束,释放锁 ");

            /**
             * 即将释放锁
             */
        }

        /**
         * 所以上述代码块是 先拿到锁,释放,然后又拿到了锁,再释放
         */


        System.out.println(Thread.currentThread().getName() + " wait结束代码块 \n\n\n");


    }

}

NotifyAllRunnable:

public class NotifyAllRunnable implements Runnable{

    @Override
    public void run() {

        synchronized (Main.waitObject) {

            System.out.println("NotifyAllRunnable begin");
            Main.waitObject.notifyAll();
            System.out.println("NotifyAllRunnable notify");

            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println("NotifyAllRunnable 即将释放锁");
        }

        System.out.println("NotifyAllRunnable 释放锁完毕 \n\n\n");

    }
}

执行结果为:

waitThread1 开始wait 
waitThread2 开始wait 
NotifyAllRunnable begin
NotifyAllRunnable notifyAll
NotifyAllRunnable 即将释放锁
NotifyAllRunnable 释放锁完毕 



waitThread2 wait结束,又获取到锁 
waitThread2 wait sleep 结束,释放锁 
waitThread2 wait结束代码块 



waitThread1 wait结束,又获取到锁 
waitThread1 wait sleep 结束,释放锁 
waitThread1 wait结束代码块 

  可以看到,NotifyAllRunnable 虽然已经notifyAll()来通知其它线程了,但是其他等待线程只是由等待队列进入了锁队列,NotifyAllRunnable还持有锁,所以在其 sleep 了一段时间之后,释放了锁。waitThread才能获取到锁,注意其中一个 waitThread A 获取到锁之后,并未马上释放,而是也等待了一段时间,另一个 waitThread B 依旧在锁队列。只有在 waitThread A 执行完同步代码块之后,才会把这个对象的锁释放,waitThread B才会获取到锁。所以我们notify()或者notifyAll()后wait的线程可能不会马上执行。

 

二、wait(long,int)方法:

  这个方法有点意思了就,wait(long,int)之后,进入等待队列,注意如果在指定时间内收到了notifyAll,那么会跟上述一样进入锁队列,否则会在超时之后进入到锁队列中。
  wait(long,int),第一个参数是要等到的毫秒数,第二个是纳秒数,第二个是为了更精确,指定为1即可,
  比如,想等待2秒,即2000ms,wait(2000, 1) 即可。

public class WaitTimeMain {


    public static final Object waitTimeObject = new Object();


    public static void main(String[] args) {

        Thread waitTimeThread = new Thread(new WaitTimeRunnable(), "waitTimeThread");
        waitTimeThread.start();


        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Thread syncThread = new Thread(new SyncRunnable(), "SyncThread");
        syncThread.start();

    }

}

public class WaitTimeRunnable implements Runnable{

    @Override
    public void run() {

        synchronized (WaitTimeMain.waitTimeObject) {

            long beginTime = System.currentTimeMillis();
            System.out.println(" WaitTimeRunnable wait开始,当前时间" + beginTime);

            try {
                WaitTimeMain.waitTimeObject.wait(2000, 1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            long endTime = System.currentTimeMillis();
            System.out.println(" WaitTimeRunnable wait完毕 , 用时 "
                    + (endTime - beginTime) +  "ms");
        }

    }

}

public class SyncRunnable implements Runnable{

    @Override
    public void run() {
        synchronized (WaitTimeMain.waitTimeObject) {
            System.out.println(" SyncRunnable 获取到锁");

            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(" SyncRunnable 5秒之后释放锁 \n\n\n");

        }

    }
}

 执行结果为:

 WaitTimeRunnable wait开始
 SyncRunnable 获取到锁

 SyncRunnable 5秒之后释放锁 

 WaitTimeRunnable wait完毕 , 用时 5112ms

  可以看到,虽然WaitTimeRunnable没有收到通知,2s之后自动进入锁队列,但是现在已有线程获取到锁,其要等待锁释放,即5秒之后才完成。

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Java中的waitnotify多线程编程中的两个重要方法,用于线程之间的协作和通信。 wait方法可以使当前线程进入等待状态,直到其他线程调用notifynotifyAll方法唤醒它。在调用wait方法时,当前线程会释放它所持有的锁,以便其他线程可以访问共享资源。 notify方法用于唤醒一个处于等待状态的线程,如果有多个线程在等待,则只会唤醒其中一个线程。notifyAll方法则会唤醒所有处于等待状态的线程。 waitnotify方法必须在同步块中使用,即在使用这两个方法的对象上获取锁。否则会抛出IllegalMonitorStateException异常。 使用waitnotify方法可以实现线程之间的协作和通信,例如生产者消费者模型。在生产者消费者模型中,生产者线程生产数据并将其放入共享队列中,消费者线程从队列中取出数据并进行消费。当队列为空时,消费者线程需要等待生产者线程生产数据,此时可以使用wait方法使消费者线程进入等待状态。当生产者线程生产数据并将其放入队列中时,可以使用notify方法唤醒处于等待状态的消费者线程。 ### 回答2: Java 多线程中的 waitnotify 是两个非常重要的方法,它们可以帮助线程之间达成协作,实现复杂的操作。wait 方法用于让当前线程进入等待状态,直到其他线程通过 notify 方法通知它继续执行。notify 方法则用于唤醒一个等待状态的线程,使其继续执行。 wait 方法 wait 方法用于让当前线程进入等待状态,直到其他线程通过 notifynotifyAll 方法唤醒它。wait 方法需要在 synchronized 代码块中使用,否则会抛出 IllegalMonitorStateException 异常。在进入等待状态之后,线程将释放锁,并且进入一个等待池中,等待其他线程调用 notifynotifyAll 方法唤醒它。 notify 方法 notify 方法用于唤醒一个等待状态的线程,使其继续执行。notify 方法同样需要在 synchronized 代码块中使用,否则同样会抛出 IllegalMonitorStateException 异常。当一个线程调用 notify 方法时,等待池中的线程将会被唤醒,但是它们不能马上继续执行,必须等待当前线程释放锁。如果有多个线程在等待池中,notify 方法只会唤醒其中一个线程,具体唤醒哪个线程是随机的。 notifyAll 方法 notifyAll 方法与 notify 方法类似,但是它会唤醒所有等待池中的线程。notifyAll 方法同样需要在 synchronized 代码块中使用。 使用 waitnotify 实现线程协作 waitnotify 方法可以用来实现线程之间的协作,例如生产者和消费者问题。假设我们有一个共享的队列,生产者向队列中添加数据,消费者从队列中取出数据。如果队列已经满了,生产者就需要等待消费者取走数据,如果队列是空的,消费者就需要等待生产者加入新数据。 在这个问题中,我们可以使用 waitnotify 方法来实现线程之间的协作,代码如下: ``` public class Queue { private final List<Integer> items = new LinkedList<>(); private static final int MAX_SIZE = 10; public synchronized void produce(int item) throws InterruptedException { while (items.size() == MAX_SIZE) { wait(); } items.add(item); notify(); } public synchronized int consume() throws InterruptedException { while (items.isEmpty()) { wait(); } int item = items.remove(0); notify(); return item; } } public class Producer implements Runnable { private final Queue queue; public Producer(Queue queue) { this.queue = queue; } public void run() { for (int i = 0; i < 20; i++) { try { queue.produce(i); System.out.println("Produced: " + i); } catch (InterruptedException ex) { ex.printStackTrace(); } } } } public class Consumer implements Runnable { private final Queue queue; public Consumer(Queue queue) { this.queue = queue; } public void run() { for (int i = 0; i < 20; i++) { try { int item = queue.consume(); System.out.println("Consumed: " + item); } catch (InterruptedException ex) { ex.printStackTrace(); } } } } public class Main { public static void main(String[] args) throws InterruptedException { Queue queue = new Queue(); Thread producer = new Thread(new Producer(queue)); Thread consumer = new Thread(new Consumer(queue)); producer.start(); consumer.start(); producer.join(); consumer.join(); } } ``` 在这个示例代码中,我们创建了一个 Queue 类,它有两个方法 produce 和 consume 用于生产和消费数据。在 produce 方法中,我们使用 while 循环来等待队列不满,如果队列已经满了,就调用 wait 方法进入等待状态。在 consume 方法中,我们使用 while 循环来等待队列不空,如果队列是空的,就调用 wait 方法进入等待状态。在生产新数据或者消费数据之后,我们都调用 notify 方法来唤醒等待池中的线程。 最后,我们可以使用 Producer 和 Consumer 类来生产和消费数据,它们分别运行在不同的线程中。在运行这个程序时,生产者将不断生产数据,消费者将不断消费数据,一直到数据生产完毕为止。在这个过程中,生产者和消费者之间通过 waitnotify 方法实现了线程之间的协作。 ### 回答3: Java是一种支持多线程的编程语言,在多线程编程过程中,一个线程可能需要等待另一个线程的某个条件满足后才能继续执行。Java提供了waitnotify来实现线程之间的协作。 wait:使当前线程进入等待状态,释放对象的锁,直到其他线程调用notifynotifyAll方法唤醒它。wait方法必须在持有对象锁的情况下调用,否则会抛出IllegalMonitorStateException异常。 notify:唤醒一个处于等待状态的线程,如果有多个线程等待,则只会唤醒其中一个,具体唤醒哪个线程无法预测。 notifyAll:唤醒所有处于等待状态的线程。 waitnotify必须在同步代码块中调用,并且针对同一个对象。waitnotify的调用顺序也非常重要,如果先调用了notify而没有等待线程,会导致唤醒失效。 在多线程编程中,waitnotify常常用于生产者和消费者模式中的线程之间的通信,生产者线程在生产完毕后调用notify方法唤醒消费者线程来消费数据,消费者线程在消费完毕后调用wait方法等待下一个生产者线程的唤醒。 waitnotify的使用需要谨慎,如果使用不当,会导致死锁或线程饥饿等问题。同时,在Java SE 5之后,Java提供了更加高级的线程库,如ReentrantLock、Condition等,可以更加方便和安全地实现线程之间的协作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值