黑马程序员技术交流社区

标题: 【面试回顾】面试和多线程的一点心得 [打印本页]

作者: ameanboy    时间: 2016-4-2 20:14
标题: 【面试回顾】面试和多线程的一点心得
本帖最后由 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的改变对于主线程来说都是可见的?





作者: ameanboy    时间: 2016-4-2 20:22
写的很乱,主要是这文本编辑器实在是捉摸不透,总是吃我文字和代码,我也懒得写了,英语好的同学,可以看看oracle的多线程教学,字字珠玑
http://docs.oracle.com/javase/tutorial/essential/concurrency/
作者: q312092921    时间: 2016-4-2 23:06
哦这个不错,谢谢楼主的分享~
作者: 爱拼的小伙子    时间: 2016-4-5 20:02
不错哦            
作者: NB的笨小孩    时间: 2016-4-5 20:56
这是Java基础视频里面老师讲过的题,以后得多加练习
作者: 蟑螂小强    时间: 2016-4-5 20:59
谢谢 已收藏
作者: sasyun    时间: 2016-6-15 12:53
感谢分享               
作者: tianzhu2725    时间: 2016-6-15 13:43
谢谢分享 辛苦了
作者: jianhua0798    时间: 2016-6-15 20:01
加油,黑马
作者: 云袭    时间: 2016-6-15 20:18
fighting..
作者: cliangtime    时间: 2016-6-15 21:25
加油加油
作者: dubei1993    时间: 2016-6-16 00:25
if else这种思路楼主想过吗?静态变量控制
作者: dubei1993    时间: 2016-6-16 00:27
不管哪个线程进来,都会走上一次不同的。不用waitnotify。
作者: middasong    时间: 2016-6-16 00:40
感谢分享哦~感觉会有用的
作者: 安静的喝会奶    时间: 2016-6-16 09:07
进来看看
作者: 晓寒轻    时间: 2016-6-16 18:28
没看懂,哎,俺水平不行啊
作者: 644313561    时间: 2016-6-16 23:05
谢谢分享,收藏先,待后面认真看
作者: 戎马生涯    时间: 2016-6-17 00:11
不错!感谢分享~~~
作者: haizi004    时间: 2016-6-17 16:18
感谢楼主分享。
作者: 443785417    时间: 2016-6-17 21:44
verygood         




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2