1、写一个包子生产消费案例:一次生产或消费一个包子,有包子就消费,没有就生产。(部分代码参考传智播客刘意2015Java基础视频讲义)

1.1 写一个Baozi.class,包含main()方法,用来测试

package com.oy.demo3;

/*
* 包子生产消费案例:一次生产或消费一个包子,有包子就消费,没有就生产。
*/
public class Baozi {
// 默认是flag,表示没有包子,需要生产线程来生产包子;如果是true,说明有包子,需要消费端来消费包子。
public boolean flag; // 计数,当前在生产或消费第n个包子
public int count = 0; public static void main(String[] args) {
// 创建共享对象
Baozi s = new Baozi(); // 在外界把共享对象创建出来,通过构造方法传递给其他的类。这样st、gt1、gt2就共享s对象。
SetThread st = new SetThread(s);
GetThread gt1 = new GetThread(s);
GetThread gt2 = new GetThread(s); // 线程类
Thread t1 = new Thread(st);
Thread t2 = new Thread(gt1);
Thread t3 = new Thread(gt2); // 启动线程
t1.start();
t2.start();
t3.start();
}
}

1.2 生产包子的线程类 SetThread.class

package com.oy.demo3;

public class SetThread implements Runnable {
private Baozi s; public SetThread(Baozi s) {
this.s = s;
} @Override
public void run() {
while (true) {
synchronized (s) {
// 判断有没有
if (s.flag) { // 生产端,有就等待
try {
System.out.println("生产端:等待。。。");
s.wait(); // 等待,并且立即释放锁。将来醒过来的时候,是从这里醒过来的
System.out.println("生产端:醒过来了。。。");
} catch (InterruptedException e) {
e.printStackTrace();
}
} // 开始生产
s.count++;
System.out.println("生产第" + s.count + "包子。。。"); // 生产完后,修改标记为true
s.flag = true;
// 唤醒线程
s.notifyAll();
System.out.println("==========开始抢CPU的执行权==========");
}
}
}
}

1.3 消费包子的线程类 GetThread.class

package com.oy.demo3;

public class GetThread implements Runnable {
private Baozi s; public GetThread(Baozi s) {
this.s = s;
} @Override
public void run() {
while (true) {
synchronized (s) {
while (!s.flag) { // 消费端,没有就等待
try {
System.out.println(Thread.currentThread().getName() + "消费端:等待。。。");
s.wait(); // 等待,并且立即释放锁。将来醒过来的时候,是从这里醒过来的
System.out.println(Thread.currentThread().getName() + "消费端:醒过来了。。。");
} catch (InterruptedException e) {
e.printStackTrace();
}
} // 开始消费
System.out.println(Thread.currentThread().getName() + "消费第" + s.count + "个包子"); // 消费完了,修改标记为false
s.flag = false;
// 唤醒线程
s.notifyAll();
System.out.println("==========开始抢CPU的执行权==========");
}
}
}
}

2、测试结果(只选择了控制台打印的部分结果):

==========开始抢CPU的执行权==========
生产端:等待。。。
Thread-2消费端:醒过来了。。。
Thread-2消费第6806个包子 // Thread-2消费完了第6806个包子,然后唤醒等待的线程;
==========开始抢CPU的执行权========== // 然后3个线程开始抢CPU的执行权
Thread-2消费端:等待。。。        // 消费端线程Thread-2抢到了,但是此时没有包子了,所以等待
Thread-1消费端:醒过来了。。。      // 然后,消费端线程Thread-1抢到了执行权,在原来wait()方法的地方醒过来,
                      // 执行wait()方法后面的代码System.out.println(Thread.currentThread().getName() + "消费端:醒过来了。。。")
Thread-1消费端:等待。。。        // 然后继续while循环判断,由于此时没有包子,所以等待;
                      // 如果把while改成if,就不会判断是否有包子,直接执行后面的代码消费包子,
                      // 此时并没有包子了,这就产生了错误(同一个包子被消费了两次)。
生产端:醒过来了。。。
生产第6807包子。。。
==========开始抢CPU的执行权==========

3、对测试结果的分析:

  3.1 首先明确,生产端开启了一个线程,消费端开启了两个线程。

  3.2 Thread-2消费完了第6806个包子,然后唤醒等待的线程;然后3个线程开始抢CPU的执行权,消费端线程Thread-2抢到了,但是此时没有包子了,所以等待;

  3.3 然后,消费端线程Thread-1抢到了执行权,在原来wait()方法的地方醒过来,执行wait()方法后面的代码System.out.println(Thread.currentThread().getName() + "消费端:醒过来了。。。"),然后继续while循环判断,由于此时没有包子,所以等待。如果把while改成if,就不会判断是否有包子,直接执行后面的代码消费包子,此时并没有包子了,这就产生了错误(同一个包子被消费了两次)。

  3.4 综上所述:wait()方法套在while循环中,线程下次醒过来后会继续进行循环,判断条件是否满足,满足就重新等待。

  3.5 由于生产端只开启了一个线程,所以将wait()方法套在if代码块中也是可以的,当然使用while也可以。

java多线程wait()方法必须放在while循环里面的原因探析的更多相关文章

  1. Java 多线程 sleep方法与wait方法的区别

    sleep方法会使线程暂停执行一段时间,wait方法会阻塞线程,直到被唤醒或等待时间超时. 两者区别具体如下: 1 原理不同 sleep方法是Thread类的静态方法,使线程暂停执行一段时间,等到计时 ...

  2. Java多线程-run方法与start方法的区别

    package com.interview; /** * java多线程的两种实现方式以及run.start方法的区别 * @author MEI.LIU * */ public class Thre ...

  3. Java多线程-join方法

    thread.Join把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程.比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B. 具体例子看链接 ...

  4. Java——多线程之方法详解

    Java多线程系列文章是Java多线程的详解介绍,对多线程还不熟悉的同学可以先去看一下我的这篇博客Java基础系列3:多线程超详细总结,这篇博客从宏观层面介绍了多线程的整体概况,接下来的几篇文章是对多 ...

  5. Java多线程优化方法及使用方式

    一.多线程介绍 在编程中,我们不可逃避的会遇到多线程的编程问题,因为在大多数的业务系统中需要并发处理,如果是在并发的场景中,多线程就非常重要了.另外,我们在面试的时候,面试官通常也会问到我们关于多线程 ...

  6. java 多线程 Synchronized方法和方法块 synchronized(this)和synchronized(object)的理解

    synchronized 关键字,它包括两种用法:synchronized 方法和 synchronized 块. 1. synchronized 方法:通过在方法声明中加入 synchronized ...

  7. java多线程 join方法以及优先级方法

    /*join:当A线程执行到了B线程的.join()方法时,A就会等待.等B线程都执行完,A才会执行. join可以用来临时加入线程执行. 1.线程使用join方法,主线程就停下,等它执行完,那么如果 ...

  8. 2017第45周一java多线程创建方法

    1. 从JDK1.5开始,Java提供了3种方式来创建,启动多线程: Ø 继承Thread类来创建线程类,重写run()方法作为线程执行体. Ø 实现Runnable接口来创建线程类,重写run()方 ...

  9. Java 多线程 interrupt方法

    interrupt 下面是interrupt方法的文档的一部分: * <p> If this thread is blocked in an invocation of the {@lin ...

随机推荐

  1. LOJ6282 数列分块入门6(分块+暴力)

    真是暴力 #include <cstdio> #include <algorithm> #include <cstring> #include <vector ...

  2. 【C#】委托的发展

    "用事件去处理程序, 进而解决问题" ---- 委托的目的 委托早在C#2的时候就已经初具模型, 但是并不是特别灵活, 制止C#3才在代码中被广泛使用. C#4中泛型委托, C#5 ...

  3. entity framework浅谈

    1. 什么是EF 微软提供的ORM工具. ORM让开发人员节省数据库访问代码的时间. 将更多的时间放在业务逻辑层面上. 开发人员使用linq语言, 对数据库进行操作. 2. EF的使用场景 EF有三种 ...

  4. Twitter开发2

    There are different API families The standard (free) Twitter APIs consist of REST APIs and Streaming ...

  5. python时间

    #!/usr/bin/python # -*- coding: utf-8 -*- import sys import time import datetime line="Wed 11/2 ...

  6. 接口自动化python

    !/usr/bin/env python coding=utf-8 Todo:接口自动化测试 Author:归根落叶 Blog:http://this.ispenn.com import json i ...

  7. c++中static的用法详解

    C 语言的 static 关键字有三种(具体来说是两种)用途: 1. 静态局部变量:用于函数体内部修饰变量,这种变量的生存期长于该函数. int foo(){ static int i = 1; // ...

  8. 进度条的制作-python

    import time,sys def view_bar(num, total): rate = float(num) / float(total) rate_num = int(rate * 100 ...

  9. PHP运算符优先级

    if (!$a = $b) { // todo } if (!($a = $b)) { // todo } if ($a = !$b) { // todo } if ($a = (!$b)) { // ...

  10. ts问题处理(2): 'Promise' only refers to a type, but is being used as a value here.

    error TS2585: 'Promise' only refers to a type, but is being used as a value here. Do you need to cha ...