一道面试题:

启动两个线程, 一个输出 1,3,5,7…99, 另一个输出 2,4,6,8…100 最后 STDOUT 中按序输出 1,2,3,4,5…100

错误实现1:

  1. public class NotifyErrorTest {
  2. private int i = 1;
  3. Thread t1 = new Thread(){
  4. @Override
  5. public void run() {
  6. while (true) {
  7. synchronized (this) {
  8. notify();
  9. if (i <= 100) {
  10. System.out.println(currentThread().getName() + ":" + i);
  11. i++;
  12. try {
  13. wait();
  14. } catch (InterruptedException e) {
  15. e.printStackTrace();
  16. }
  17. }
  18. }
  19. }
  20. }
  21. };
  22. Thread t2 = new Thread(){
  23. @Override
  24. public void run() {
  25. while (true) {
  26. synchronized (this) {
  27. notify();
  28. if (i <= 100) {
  29. System.out.println(currentThread().getName() + ":" + i);
  30. i++;
  31. try {
  32. wait();
  33. } catch (InterruptedException e) {
  34. e.printStackTrace();
  35. }
  36. }
  37. }
  38. }
  39. }
  40. };
  41. public static void main(String[] args){
  42. NotifyErrorTest test = new NotifyErrorTest();
  43. test.t1.start();
  44. test.t2.start();
  45. }
  46. }

结果:

  1. Thread-0:1
  2. Thread-1:1

打印出这两个后,线程就一直被挂起了。为什么会这样呢。

先不考虑这种难看的重复代码需不需要重构,本身代码就有问题,虽然看起来都用了this,但是其实两个this所表示的含义不同,我们两个线程里面加上如下代码

  1. System.out.println(this.getClass());

会发现打印出

  1. class pers.marscheng.thread.NotifyErrorTest$1
  2. class pers.marscheng.thread.NotifyErrorTest$2

原来两个this不是同一个对象,匿名类会生成新的对象,所以导致两个线程获取的monitor锁是不同的。这就导致wait()方法调用之后,两个线程都被挂起,但是再也没人能把他们唤醒,而且由于锁不同,两个线程都同时执行了,打印出的都是1。

正确实现:

  1. public class NotifyTest implements Runnable {
  2. int i = 1;
  3. public static void main(String[] args) {
  4. NotifyTest test = new NotifyTest();
  5. Thread t1 = new Thread(test);
  6. Thread t2 = new Thread(test);
  7. t1.start();
  8. t2.start();
  9. }
  10. @Override
  11. public void run() {
  12. while (true) {
  13. synchronized (this) {
  14. this.notify();
  15. if (i <= 100) {
  16. String threadName = Thread.currentThread().getName();
  17. System.out.println(threadName + ":" + i);
  18. i++;
  19. try {
  20. this.wait();
  21. } catch (InterruptedException e) {
  22. e.printStackTrace();
  23. }
  24. }
  25. }
  26. }
  27. }
  28. }

通过condition实现:

  1. public class ConditionTest implements Runnable{
  2. Lock lock = new ReentrantLock();
  3. Condition condition = lock.newCondition();
  4. int i = 1;
  5. @Override
  6. public void run() {
  7. try {
  8. lock.lock();
  9. while (true) {
  10. condition.signal();
  11. if (i <= 100) {
  12. System.out.println(Thread.currentThread().getName() + ":" + i);
  13. i++;
  14. try {
  15. condition.await();
  16. } catch (InterruptedException e) {
  17. e.printStackTrace();
  18. }
  19. }
  20. }
  21. } finally {
  22. lock.unlock();
  23. }
  24. }
  25. public static void main(String[] args) {
  26. ConditionTest test = new ConditionTest();
  27. Thread t1 = new Thread(test);
  28. Thread t2 = new Thread(test);
  29. t1.start();
  30. t2.start();
  31. }
  32. }

拓展:

启动三个线程, 一个输出 1,4,7,10…100, 一个输出 2,5,8,11…101,最后一个暑促3,6,9,12...102 最后 STDOUT 中按序输出 1,2,3,4,5…102

实现:

  1. public class NotifyTest2 implements Runnable {
  2. private Object prev;
  3. private Object self;
  4. AtomicInteger i;
  5. private NotifyTest2(AtomicInteger num,Object prev, Object self) {
  6. this.i = num;
  7. this.prev = prev;
  8. this.self = self;
  9. }
  10. @Override
  11. public void run() {
  12. while (true) {
  13. synchronized (prev) {
  14. synchronized (self) {
  15. if (i.get() <= 102) {
  16. System.out.println(Thread.currentThread().getName() + ":" + i.get());
  17. i.getAndIncrement();
  18. self.notify();
  19. }
  20. }
  21. try {
  22. prev.wait();
  23. } catch (InterruptedException e) {
  24. e.printStackTrace();
  25. }
  26. }
  27. }
  28. }
  29. public static void main(String[] args) {
  30. Object a = new Object();
  31. Object b = new Object();
  32. Object c = new Object();
  33. AtomicInteger num = new AtomicInteger(1);
  34. NotifyTest2 testA = new NotifyTest2(num,c,a);
  35. NotifyTest2 testB = new NotifyTest2(num,a,b);
  36. NotifyTest2 testC = new NotifyTest2(num,b,c);
  37. new Thread(testA).start();
  38. new Thread(testB).start();
  39. new Thread(testC).start();
  40. }
  41. }

利用AtomicInteger做为共享变量。

wait-notify模型面试题的更多相关文章

  1. JVM内存模型和面试题解析

    一.JVM运行时区域 其中, 线程私有的:程序计数器,虚拟机栈,本地方法栈 线程共享的:堆,方法区,直接内存 1 程序计数器 程序计数器是一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示 ...

  2. 不止面试02-JVM内存模型面试题详解

    第一部分:面试题 本篇文章我们将尝试回答以下问题: 描述一下jvm的内存结构 描述一下jvm的内存模型 谈一下你对常量池的理解 什么情况下会发生栈内存溢出?和内存溢出有什么不同? String str ...

  3. Java多线程与并发基础

    CS-LogN思维导图:记录专业基础 面试题 开源地址:https://github.com/FISHers6/CS-LogN 多线程与并发基础 实现多线程 面试题1:有几种实现线程的方法,分别是什么 ...

  4. CCS+C6678LE开发记录11:多核协作(IPC)入门

    为更好地发挥C6678的多核性能,需要用到多核协作.幸运的是,我们可以使用官方提供的IPC模块. IPC=Inter-Processor Communication, 核间通信,粗略来说就是多核之间进 ...

  5. 详解CurrentHashMap之预习篇

    CurrentHashMap的出现时为了解决HashMap的高并发导致OOM的缺陷,并且能够保证高性能读取.那么解读CurrentHashMap需要具备哪些知识的呢? HashMap 解读 Java ...

  6. 关于Java高并发编程你需要知道的“升段攻略”

    关于Java高并发编程你需要知道的"升段攻略" 基础 Thread对象调用start()方法包含的步骤 通过jvm告诉操作系统创建Thread 操作系统开辟内存并使用Windows ...

  7. Java基础(补充)

    为什么 Java 中只有值传递? 开始之前,我们先来搞懂下面这两个概念: 形参&实参 值传递&引用传递 形参&实参 方法的定义可能会用到 参数(有参的方法),参数在程序语言中分 ...

  8. 如何在 Java 中正确使用 wait, notify 和 notifyAll – 以生产者消费者模型为例

    wait, notify 和 notifyAll,这些在多线程中被经常用到的保留关键字,在实际开发的时候很多时候却并没有被大家重视.本文对这些关键字的使用进行了描述. 在 Java 中可以用 wait ...

  9. 【java线程系列】java线程系列之线程间的交互wait()/notify()/notifyAll()及生产者与消费者模型

    关于线程,博主写过java线程详解基本上把java线程的基础知识都讲解到位了,但是那还远远不够,多线程的存在就是为了让多个线程去协作来完成某一具体任务,比如生产者与消费者模型,因此了解线程间的协作是非 ...

随机推荐

  1. ie8 js编译器对象为空或不是对象的一个小问题

    昨天在遍历json串的时候碰到了如下图所示的问题,除ie8以下版本的浏览器运行都是正常的, 部分代码如下: 1 var Workmodel=function(){ 2 model_json=[ 3 { ...

  2. android UI 操作 不要在子线程中操作UI

    不管是android ,还是 ios ,请不要在子线程中操作UI,有时有些崩溃,从报错上看不出什么原因,就有可能是子线程操作了UI:切记,切记! 请放在主线程例: activity.runOnUiTh ...

  3. cglib动态代理(即AOP)

    Computer.java package com.wh.spring_aop; public class Computer { public void playLOL(){ System.out.p ...

  4. jsp之servlet模板问题

    如果你在web项目下创建一个Servlet类,那么它会自带很多东西,比如有很多的注释,还有很多out.println()语句等.可能这些东西都不是你需要,这样看起来就会比较的令人不爽.下面的话就教大家 ...

  5. jQuery——多库共存

    多库共存:jQuery占用了$ 和jQuery这两个变量.当在同一个页面中引用了jQuery这个js库,并且引用的其他库(或者其他版本的jQuery库)中也用到了$或者jQuery这两个变量,那么,要 ...

  6. jQuery——节点操作

    创建节点 1.$():创建一个li标签 $("<li class='aaa'>我是li标签</li>") 2.html():创建一个li标签并同时添加到ul ...

  7. SQL基本操作——约束

    我们将主要探讨以下几种约束: 1.NOT NULL 2.UNIQUE 3.PRIMARY KEY 4.FOREIGN KEY 5.CHECK 6.DEFAULT SQL NOTNULL约束:NOT N ...

  8. 重绘DataGridView的DataGridViewCheckBoxCell控件

    最近项目中要用到在DataGridView单元格里面放置一个带有文本的 DataGridViewCheckBoxCell控件但原有 的是不支持的然后我就想着重写个 DataGridViewCheckB ...

  9. RTL Compiler之synthesis steps

    1 synthesis steps 1) Search Paths rc:/> set_attribute lib_search_path path / rc:/> set_attribu ...

  10. RadioButtonList的兩種實現方式

    一種是修改ItemTemplate,即ListBoxItem裏面的内容 <ListBox ItemsSource="{Binding}"> <ListBox.It ...