A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

本帖最后由 ameanboy 于 2016-4-2 23:07 编辑

面试的时候代码题考到了多线程,很悲催的没有过,回头看看自己确实基本知识不扎实,另外准备面试的时候跑偏了方向,研究算法数据结构研究了半天。。。结果面试的重点考点是对于java基础知识,和常用包的掌握,而java使用时最重要的原则,就是donot reinvent the wheel(不要重新发明轮子),基础知识和常用包的掌握非常重要,给正在准备面试的同学提醒一下。

我的面试考题很简单,第二题是要求用两个线程模拟两个人往银行交替存钱,当时我的代码是这样的:
  1. public class Test02 {

  2.     public static void main(String[] args) {
  3.         // 测试
  4.         final Person p1 = new Person("张三", 1);
  5.         final Person p2 = new Person("李四", 0);

  6.         Thread t1 = new Thread() {
  7.             public void run() {
  8.                 while (true) {
  9.                     try {
  10.                         if (Person.getFlag() == p1.getTargetFlag()) {
  11.                             p1.savingMoney();
  12.                             Person.setFlag(0);
  13.                             p1.notifyAll();        //错误1:锁对象已经在执行完savingMoney后被释放了,发生IllegalMonitorState异常
  14.                         } else {
  15.                             p1.wait();                //错误2:锁对象已经在执行完savingMoney后被释放了,发生IllegalMonitorState异常
  16.                         }        
  17.                     } catch (Exception e) {
  18.                         e.printStackTrace();
  19.                     }
  20.                 }

  21.             }
  22.         };

  23.         Thread t2 = new Thread() {
  24.             public void run() {

  25.                 while (true) {
  26.                     try {
  27.                         if (Person.getFlag() == p2.getTargetFlag()) {
  28.                             p2.savingMoney();
  29.                             Person.setFlag(1);
  30.                             p2.notifyAll();        //错误1a同上
  31.                         } else {
  32.                             p2.wait();                //错误2a同上
  33.                         }
  34.                     } catch (Exception e) {
  35.                         e.printStackTrace();
  36.                     }
  37.                 }
  38.             }
  39.         };
  40.         t1.start();
  41.         t2.start();
  42.     }
  43. }

  44. class Person {
  45.     private String name;
  46.     private static int flag;
  47.     private int targetFlag;

  48.     static {
  49.         flag = 0;
  50.     }

  51.     Person(String name, int targetFlag) {
  52.         this.name = name;
  53.         this.targetFlag = targetFlag;
  54.     }

  55.     public String getName() {
  56.         return name;
  57.     }

  58.     public int getTargetFlag() {
  59.         return targetFlag;
  60.     }

  61.     public synchronized void savingMoney() throws Exception {        //此处的synchronzied关键字的锁对象是this
  62.         System.out.print(this.getName());
  63.         System.out.println("向银行存入500元");
  64.     }

  65.     public static int getFlag() {
  66.         return flag;
  67.     }

  68.     public static void setFlag(int flag) {
  69.         Person.flag = flag;
  70.     }
  71. }
复制代码


于是我痛下心来,把oracle的concurrency这一章仔仔细细的读了一遍(不得不说我还是太菜了,基本上每个字都够我google一遍),但是收获颇丰,根据教程,其实根本上犯了两个错误,没有搞清楚锁对象的作用,以及synchronized关键字的真正作用,总结起来,就一句话
  1. When a thread invokes d.wait, it must own the intrinsic lock for d
复制代码

1. 在调用wait()和notify()方法时,一定要确认该线程持有的锁对象!
2. synchronized关键字在执行完毕后,会自动释放锁对象,换句话说,只有在synchronized的作用域范围内调用锁对象的wait(),notify()才是有意义的

所以,我的代码犯了如下错误:
p1,p2线程中的savingMoney方法,锁对象实际上是自己,所以在调用了自身的wait()和notify()方法后,没有任何在等待该锁对象的线程响应

*这跟基础课多线程中的那个交替打印的小程序不同,那个程序是一个对象四个print方法,所以针对那个程序来说,在方法前加上synchronized关键字是完全没有问题的

另外,留两道oracle教程中的问题,大家可以看看自己对于Thread实现原理是否掌握深刻
  1. Can you pass a Thread object to Executor.execute? Would such an invocation make sense?
  2. 能否将Thread对象传入Executor并且运行?这样做是否有意义?
复制代码

查看以下代码:
  1. Compile and run BadThreads.java:

  2. public class BadThreads {

  3.     static String message;

  4.     private static class CorrectorThread
  5.         extends Thread {

  6.         public void run() {
  7.             try {
  8.                 sleep(1000);
  9.             } catch (InterruptedException e) {}
  10.             // Key statement 1:
  11.             message = "Mares do eat oats.";
  12.         }
  13.     }

  14.     public static void main(String args[])
  15.         throws InterruptedException {

  16.         (new CorrectorThread()).start();
  17.         message = "Mares do not eat oats.";
  18.         Thread.sleep(2000);
  19.         // Key statement 2:
  20.         System.out.println(message);
  21.     }
  22. }
  23. The application should print out "Mares do eat oats." Is it guaranteed to always do this? If not, why not? Would it help to change the parameters of the two invocations of Sleep? How would you guarantee that all changes to message will be visible in the main thread?
复制代码
这段代码应该始终打印出: Mares do eat oats。但是能否保证程序总是如此执行?如果不,为何?改变sleep的参数对于此目的有帮助吗?如果是你,你如何保证所有对于message的改变对于主线程来说都是可见的?




19 个回复

倒序浏览
写的很乱,主要是这文本编辑器实在是捉摸不透,总是吃我文字和代码,我也懒得写了,英语好的同学,可以看看oracle的多线程教学,字字珠玑
http://docs.oracle.com/javase/tutorial/essential/concurrency/
回复 使用道具 举报
哦这个不错,谢谢楼主的分享~
回复 使用道具 举报
不错哦            
回复 使用道具 举报
这是Java基础视频里面老师讲过的题,以后得多加练习
回复 使用道具 举报
谢谢 已收藏
回复 使用道具 举报
sasyun 中级黑马 2016-6-15 12:53:46
7#
感谢分享               
回复 使用道具 举报
谢谢分享 辛苦了
回复 使用道具 举报
加油,黑马
回复 使用道具 举报
fighting..
回复 使用道具 举报
加油加油
回复 使用道具 举报
dubei1993 来自手机 中级黑马 2016-6-16 00:25:09
12#
if else这种思路楼主想过吗?静态变量控制
回复 使用道具 举报
dubei1993 来自手机 中级黑马 2016-6-16 00:27:24
13#
不管哪个线程进来,都会走上一次不同的。不用waitnotify。
回复 使用道具 举报
感谢分享哦~感觉会有用的
回复 使用道具 举报
进来看看
回复 使用道具 举报
没看懂,哎,俺水平不行啊
回复 使用道具 举报
谢谢分享,收藏先,待后面认真看
回复 使用道具 举报
不错!感谢分享~~~
回复 使用道具 举报
感谢楼主分享。
回复 使用道具 举报
verygood         
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马