黑马程序员技术交流社区

标题: 多线程中两个售票窗口4个售票员完成售票后的意外求解 [打印本页]

作者: 崔维友    时间: 2012-11-19 13:53
标题: 多线程中两个售票窗口4个售票员完成售票后的意外求解
本帖最后由 崔维友 于 2012-11-19 15:38 编辑

       我有100张票,4个售票员卖。第一个窗口由同步代码块开售,并开启第二个窗口供同步函数开售。
代码如下:
  1. class Ticket implements Runnable {
  2.         private int tickets=100;        //票数
  3.         public boolean onlyWin=true;        //仅仅使用默认售票窗口
  4.         private boolean fucSyn=true;        //防止同步函数中同步线程死循环

  5.         public void run() {
  6.                 if (onlyWin) {
  7.                         while (true) {
  8.                                 //同步语句块,使用监工this。
  9.                                 synchronized (this) {
  10.                                         if (tickets>0) {
  11.                                                 System.out.println(Thread.currentThread().getName()+" 默认窗口售票 "+tickets--);
  12.                                         }else {
  13.                                                 System.out.println(Thread.currentThread().getName()+"售票结束");
  14.                                                 break;
  15.                                         }
  16.                                 }
  17.                         }
  18.                 }else {
  19.                         while (true) {
  20.                                 if (fucSyn) {
  21.                                         sale();
  22.                                 } else  {
  23.                                         break;
  24.                                 }
  25.                         }
  26.                 }
  27.         }

  28.         //同步函数。监工默认为this,和默认售票窗口的监工是同一个。
  29.         public synchronized void sale () {
  30.                 if (tickets>0) {
  31.                         System.out.println(Thread.currentThread().getName()+" 新开窗口售票 "+tickets--);
  32.                 }else {
  33.                         System.out.println(Thread.currentThread().getName()+"售票结束");
  34.                         fucSyn=false;
  35.                 }
  36.         }
  37. }

  38. class RunnableTest {
  39.         public static void main(String[] args)  {
  40.                 Ticket tic=new Ticket();

  41.                 new Thread(tic).start();        //第1个售票员
  42.                 new Thread(tic).start();        //第2个售票员
  43.                 //主线程有最高优先权,以上两条线程开启后处于临时状态,并非立刻执行。
  44.                 try{Thread.sleep(10);}catch (Exception e){e.printStackTrace();}
  45.                 tic.onlyWin=false;        //开启新窗口
  46.                 new Thread(tic).start();        //第3个售票员
  47.                 new Thread(tic).start();        //第4个售票员
  48.         }
  49. }
复制代码
       按照我的设想,票目售完后4个售票员都会汇报一下,但却出现了只有3个做汇报的状况,请问消失的那个售票员哪去了?



.
作者: 张学永    时间: 2012-11-19 14:55
本帖最后由 张学永 于 2012-11-19 15:04 编辑

   public void run() {
                if (onlyWin) {
                        while (true) {
                                //同步语句块,使用监工this。
                                synchronized (this) {
                                        if (tickets>0) {
                                                System.out.println(Thread.currentThread().getName()+" 默认窗口售票 "+tickets--);
                                        }else {
                                                System.out.println(Thread.currentThread().getName()+"售票结束");
                                                break;
                                        }
                                }
                        }
                }else {
                        while (true) {
                                if (fucSyn) {//也就是这里,当标记改变的时候,不会执行sale()方法,会执行下面的break
                                        sale();
                                } else  {
                                        break;//可以在这里的break上面写个输出语句System.out.println("消失的售票员")来验证,应该是会出现两次,因 为你改变标记后,有两个线程会运行到这段代码,也就是新开的窗口。消失的情况是,一个执行了上面的sale(),一个没有执行。
                                }
                        }
                }
        }

        //同步函数。监工默认为this,和默认售票窗口的监工是同一个。
        public synchronized void sale () {
                if (tickets>0) {
                        System.out.println(Thread.currentThread().getName()+" 新开窗口售票 "+tickets--);
                }else {
                        System.out.println(Thread.currentThread().getName()+"售票结束");
                        fucSyn=false;//我觉得问题出在这个标记的问题上,当这个标记改变的时候,有和谐的情况,也有不和谐的情况,就是后开的线程里一个线程改变了标记,另一个新开的的线程窗口读到了此标记,则不会执行sale()方法,会直接执行break;而你的“售票结束”语句是定义在sale()方法上的,所以没有执行;
                }
        }

解决办法就是:你可以将sale()方法里面的System.out.println(Thread.currentThread().getName()+"售票结束")
剪切到
while (true) {
                                if (fucSyn) {
                                        sale();
                                } else  {
                                        System.out.println(Thread.currentThread().getName()+"售票结束");
                                        break;
                                }
                        }
这样就可以了~

作者: 刘学宾    时间: 2012-11-19 15:19
  1. else
  2. {
  3.         while (true)
  4.        {
  5.             if (fucSyn)
  6.                sale();
  7.             else
  8.                break;
  9.          }
  10. }
复制代码
问题在你地20,21这几行代码中。
在第二个窗口有两个线程卖票,设为A和B。
假如A线程执行到 if (fucSyn)  判断之前,而此时票已经卖完,并且B线程已经fucSyn=false语句,
那么A线程继续执行判断fucSyn为false则跳到 else 执行break语句,这样A线程就没有再次进入
sale()中而没能打印出“售票结束”。
当然这种情况很少出现,但却是存在漏洞。
你可以System.out.println(Thread.currentThread().getName()+"售票结束");放在break语句前面,
注意这两条语句要被大括号括起来。这样就可以避免上述情况。


作者: 崔维友    时间: 2012-11-19 15:36
本帖最后由 崔维友 于 2012-11-19 15:38 编辑
张学永 发表于 2012-11-19 14:55
public void run() {
                if (onlyWin) {
                        while (true) {

谢谢!的确如你所说可以看到消失的线程去向。讲的很透彻,{:soso_e181:}
作者: 崔维友    时间: 2012-11-19 15:37
刘学宾 发表于 2012-11-19 15:19
问题在你地20,21这几行代码中。
在第二个窗口有两个线程卖票,设为A和B。
假如A线程执行到 if (fucSyn)  判 ...

谢谢!你的分析非常到位。:handshake
作者: 崔维友    时间: 2012-11-19 15:44

---------------------------------------------------------------------
感谢做出细致回复的楼上两位以及评分的老师!





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