黑马程序员技术交流社区

标题: 多线程第一种方式中的卖票中的小问题 [打印本页]

作者: 贺靖轩    时间: 2013-4-12 11:00
标题: 多线程第一种方式中的卖票中的小问题
问题:
1.为什么  Thread-0end-----97 已经输出了,后面还要输出 end
2.为什么  Thread-0end-----97 已经输出了,意味着票全部售出,为什么不是 Thread-0end-----100?


码:

  1. public class TicketTest extends Thread
  2. {
  3.         private static int tick=100;
  4.         static int count=0;
  5.         public void run()
  6.         {
  7.                 while(true)
  8.                 {
  9.                         if(tick>0)
  10.                         {
  11.                                 System.out.println(currentThread().getName()+"---"+tick--);
  12.                                 count++;
  13.                         }
  14.                         else
  15.                         {
  16.                                 System.out.println(currentThread().getName()+"end-----"+count);
  17.                                 break;
  18.                         }
  19.                 }
  20.         }
  21.         public static void main(String[] args)
  22.         {
  23.                 TicketTest t1=new TicketTest();
  24.                 TicketTest t2=new TicketTest();
  25.                 TicketTest t3=new TicketTest();
  26.                 TicketTest t4=new TicketTest();
  27.                
  28.                 t1.start();
  29.                 t2.start();
  30.                 t3.start();
  31.                 t4.start();
  32.         }
  33. }
复制代码
截取关键部分的输出语句:
  Thread-0---10
  Thread-0---9
  Thread-0---8
  Thread-0---7
  Thread-0---6
  Thread-0---5
  Thread-0---4
  Thread-0---3
  Thread-0---2
  Thread-0---1
  Thread-0end-----97
  Thread-3---16
  Thread-3end-----98
  Thread-1---17
  Thread-1end-----99
  Thread-2---13
  Thread-2end-----100

问题:
1.为什么  Thread-0end-----97 已经输出了,后面还要输出 end
2.为什么  Thread-0end-----97 已经输出了,意味着票全部售出,为什么不是 Thread-0end-----100?


作者: 杨冉    时间: 2013-4-12 11:39
本帖最后由 杨冉 于 2013-4-12 12:09 编辑

这是因为你没有加锁来进行线程的同步。所以是不安全的,不安全的表现就像你所说的这样了,会出现输出错乱的情况。
分析你这个输出结果发生的原因就是,你这四个线程操作的都是同样的资源(tick和count)。线程在抢占CPU资源的时候,没有将一整段本该完整执行的代码执行完(比如刚刚判断了if语句正要执行输出语句时)就被别的线程抢占了CPU资源,而自己则让出了资源,当这个线程重新获取到资源时,本应重新判断的语句没有判断就从上次没执行完的地方继续执行了(比如现在共享资源已经改变,if语句本应判断失败,但却没有判断,而是直接执行了上次没有执行的输出语句),这样就发生了共享数据的错误,出现不到100就end也就不奇怪了。
可以尝试这样加入锁:
  1. import java.util.concurrent.locks.*;

  2. public class TicketTest extends Thread {

  3. private Lock lock = new ReentrantLock();
  4. private static int tick=100;
  5. static int count=0;
  6. public void run() {
  7. while(true) {
  8. lock.lock();
  9. try {
  10. if(tick>0) {
  11. System.out.println(currentThread().getName()+"---"+tick--);
  12. count++;
  13. }
  14. else {
  15. System.out.println(currentThread().getName()+"end-----"+count);
  16. break;
  17. }
  18. }finally {
  19. lock.unlock();
  20. }
  21. }
  22. }
  23. public static void main(String[] args)
  24. {
  25. TicketTest t1=new TicketTest();
  26. TicketTest t2=new TicketTest();
  27. TicketTest t3=new TicketTest();
  28. TicketTest t4=new TicketTest();

  29. t1.start();
  30. t2.start();
  31. t3.start();
  32. t4.start();
  33. }
  34. }
复制代码

作者: 贺靖轩    时间: 2013-4-12 11:41
是不是可以换个理解方式。
对于Q1,Thread-0end-----97 已经输出了,就意味着此案例中的票确实是已经全部卖完了,tick确实是已经小于0了,但是是对于该窗口而言,因为另外3个也在进行着这个动作(TICK--),它所需要做的仅仅是打印自身所能涉及到的状态。对于票数售空,是其可以接触的内容;但是总票数在其调用结束后,却是另外三个线程需要关心的问题。
对于Q2,承接Q1中的思路。对于4线程各自来说,此时票数确实已经为0了,但是由于对CPU的资源的抢占,以及调用输出命令的延迟,使得一些没来得及输出的语句也产生了变化 ?
感觉好牵强,求正解。
作者: 张先龙    时间: 2013-4-12 11:46
本帖最后由 张先龙 于 2013-4-12 11:52 编辑

看来你没有理解多线程的意思啊
第一个问题:
你一共启动了四个线程,四个线程时并发执行的 。Thread-0end-----97 已经输出,说明该线程执行到这里时其实票已经卖完了,Thread0线程结束后,还有其他线程输出end ,是因为这四个程序是并发执行的,交替获得Cpu执行权,可能的原因是其他的线程例如Thread-2已经在Thread-0end-----97 之前执行过,将票已经卖完,还没有执行到输出语句那里就被Thread-0抢夺了CPU执行权,这时Thread-0进来执行时判断票已经卖完了,符合end的条件 ,结束。同时Thread2被唤醒,继续执行,之前未完成的部分执行完,所以会在Thread-0end-----97 输出之后还在输出。就是这个道理。

第二个问题其实的答案和第一个的原理其实是一样的。
作者: 贺靖轩    时间: 2013-4-12 11:58
恩 线程安全刚看的 才反应过来。

操作的都是同样的资源(tick和count)。线程在抢占CPU资源的时候,没有将一整段本该完整执行的代码执行完(比如刚刚判断了if语句正要执行输出语句时)就被别的线程抢占了CPU资源,而自己则让出了资源,当这个线程重新获取到资源时,本应重新判断的语句没有判断就从上次没执行完的地方继续执行了(比如现在共享资源已经改变,if语句本应判断失败,但却没有判断,而是直接执行了上次没有执行的输出语句),这样就发生了共享数据的错误,出现不到100就end也就不奇怪了。这个是理论。

你一共启动了四个线程,四个线程时并发执行的 Thread-0end-----97 已经输出,说明该线程执行到这里时其实票已经卖完了,线程结束,后面还输出end 是因为这四个程序是并发执行的,交替获得Cpu执行权,可能的原因是其他的线程例如Thread-2已经在Thread-0end-----97 之前执行过,将票已经卖完,但是被冻结,还没有执行到输出语句那里就被Thread-0抢夺CPU执行权,所以Thread-0进来执行是判断票已经卖完了,符合end的条件 ,结束。这时Thread2被唤醒,继续执行,所以会在Thread-0end-----97 输出之后还在输出。这个是实际。

归根结底是线程安全问题。
可参考
黑马程序员_毕向东_Java基础视频教程第11天-09-多线程(多线程的安全问题).avi

作者: 想学跑的猪    时间: 2013-4-12 12:07
你好,你这段代码是典型的多线程例子,在张孝祥老师的教学视频中有讲过。代码中一共有创建了四个线程,这四个线程的运行是不安全的,或者说他们不同步,出现这种,应该将if..else放到synchronized(str){}中。这样线程才会安全
作者: 打工人    时间: 2013-4-12 23:16

如果问题未解决,请继续追问,如果没有问题了,请将帖子分类 改为“已解决”,谢谢




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