黑马程序员技术交流社区

标题: 对于多线程售票例子运行结果的疑问 [打印本页]

作者: 刘渝灵    时间: 2013-9-25 17:09
标题: 对于多线程售票例子运行结果的疑问
本帖最后由 刘渝灵 于 2013-9-25 17:12 编辑

为什么会出现2次100?
//多窗口售票的例子。
public class TicketDemo
{
        public static void main(String[] args)
        {
                //用第二种方式创建多线程,仅仅只封装多线程中需要被执行的代码。此时,4个线程公用同一个资源对象。
                Ticket t = new Ticket();
                Thread t1 = new Thread(t);
                Thread t2 = new Thread(t);
                Thread t3 = new Thread(t);
                Thread t4 = new Thread(t);
                t1.start();
                t2.start();
                t3.start();
                t4.start();

        }
}
class Ticket implements Runnable
{
        private int tickets = 100 ;
        public void run()
        {
                while(true)
                {
                        if(tickets>0)
                                System.out.println(Thread.currentThread().getName()+"---"+tickets--);
                }
        }
}


上面这个例子我在运行的时候会出现下图所示的结果,即会出现2次100的情况,不知道这种情况是如何发生的?顺便吐槽一下,论坛发帖编辑工具中的代码功能真是不好用,一个字烂。

97C5EF749B594165B8B069B84CA695C6.jpg (41.56 KB, 下载次数: 25)

多线程售票运行结果

多线程售票运行结果

作者: 早知道    时间: 2013-9-25 17:16
本帖最后由 早知道 于 2013-9-25 17:22 编辑

这是线程安全问题,多线程会经常出现这种问题。解决方案就是对操作共享数据的代码块进行同步。
像你的代码可以改成

  1. <P>                                          while(true)
  2.                 {</P>
  3. <P>                         synchronized (this) {

  4.                         if(tickets>0)
  5.                                 System.out.println(Thread.currentThread().getName()+"---"+tickets--);
  6.                 }

  7.                 }</P>
复制代码

作者: 罗凯健    时间: 2013-9-25 17:19
本帖最后由 罗凯健 于 2013-9-25 17:33 编辑

因为创建了2个对象,每个对象里面有100张票,而且没进行同步,所以票就有2*100=200张~

-----题目没看,sorry,上面明显是不对的。但是我复制你的代码运行一下,居然几乎死机,而且程序不停止。我看看什么问题

作者: 刘渝灵    时间: 2013-9-25 17:24
早知道 发表于 2013-9-25 17:16
这是线程安全问题,多线程会经常出现这种问题。解决方案就是对操作共享数据的代码块进行同步。
像你的代码 ...

对共享资源的操作未同步,有安全隐患是对的,但对这个售票的例子来说,典型的状况是可能出现负的票。这个出现2次100的情况明显和负票不一样。
难道象i--这种操作不是一步完成的,在i=i-1还未完成时,已经又有另外的线程已经输出i了?

作者: 梁贺    时间: 2013-9-25 17:25
你这是线程不安全的。
  while(true)
                 {
                         if(tickets>0)
                                 System.out.println(Thread.currentThread().getName()+"---"+tickets--);
                 }

当t1.start();
    t2.start();
执行时,假如t1运行run方法执行
执行完了if(tickets>0) 这句,而未执行System.out.println(Thread.currentThread().getName()+"---"+tickets--);,此时tickets =100并且此时CUP把执行权交给了t2,t2再执行完run方法时,t1,t2都会打印出100;
极端点是t1,t2,t3,t4都会打印出100。要想解决的话就加个锁,即:
synchronized (this) {
                    while(true)

              {

                       if(tickets>0)

                                System.out.println(Thread.currentThread().getName()+"---"+tickets--);
               }

                }

希望可以帮助你~

作者: 刘渝灵    时间: 2013-9-25 17:27
罗凯健 发表于 2013-9-25 17:19
因为创建了2个对象,每个对象里面有100张票,而且没进行同步,所以票就有2*100=200张~ ...

兄弟,我只创建了一个共享资源对象即 Ticket t = new Ticket(); 4个线程都是用的这个资源创建的,你说我创建了2个对象?难道我眼花了

作者: 刘渝灵    时间: 2013-9-25 17:32
梁贺 发表于 2013-9-25 17:25
你这是线程不安全的。
  while(true)
                 {

"执行完了if(tickets>0) 这句,而未执行System.out.println(Thread.currentThread().getName()+"---"+tickets--);,此时tickets =100并且此时CUP把执行权交给了t2,t2再执行完run方法时,t1,t2都会打印出100;"
tickets--不是一步操作?可能被分开?因此会出现2次100.但是为什么只在开始的100出现呢,其他数字还是正常的。

作者: 罗凯健    时间: 2013-9-25 17:37
刘渝灵 发表于 2013-9-25 17:27
兄弟,我只创建了一个共享资源对象即 Ticket t = new Ticket(); 4个线程都是用的这个资源创建的,你说我 ...

sorry,没看题目就乱说一通了。另外说一下,你的程序没有明确的退出循环标记,我几乎死机了。我也没运行到出现2次100的情况,我再试试

作者: 简单ai...    时间: 2013-9-25 19:15
加个同步锁,这个售票的例子好像就是讲synchronized 的
作者: 暮雨    时间: 2013-9-25 19:39
对于你这种情况,我想着只是一个特殊的结果,对于多线程同时访问临界资源问题,如果不进行同步处理,就很有可能出现意想不到的结果,我想如果你再运行一下这个程序出现的就不是这个结果了。关于你这个问题,我的理解是这样的:
从结果来看,线程0和线程1第一次拿到的都是100,此时tickets 还未进行--操作,最有可能的结果是线程1首先拿到tickets ,然后还未输出,此时线程1的时间片用完(学过操作系统的应该都知道,多线程访问实际就是时间片轮转算法的一个实现),放弃cpu资源将自己放入等待队列,线程0得到cpu资源开始执行,就会得到途中前几条结果,线程0时间片用完,然后线程1又得到了cpu资源开始执行,由于刚取到的100 ,现在打印也是100,然后时间片用完,线程0又得到cpu开始执行,总之未同步的多线程操作时会出现很多种结果的。
希望可以帮到你。
作者: 刘渝灵    时间: 2013-9-26 08:35
罗凯健 发表于 2013-9-25 17:37
sorry,没看题目就乱说一通了。另外说一下,你的程序没有明确的退出循环标记,我几乎死机了。我也没运行 ...

在多线程部分,为了更明显的看到一些效果,通常就是加无限循环啊。运行结果可能和我这张图不一样这正是多线程的随机性,我自己运行也并不是每次都这样,只是出现2次100的情况发生了多次,我有些疑惑,所以才有此一问。

作者: 刘渝灵    时间: 2013-9-26 08:42
暮雨 发表于 2013-9-25 19:39
对于你这种情况,我想着只是一个特殊的结果,对于多线程同时访问临界资源问题,如果不进行同步处理,就很有 ...

你的意思就是说tickets--不是一步完成的操作,在tickets自减之前,多条线程可能已经输出tickets了?其实我就是不确定,类似i++,i--这种操作究竟是一步,还是分成多步的。如果是多步的,那么在++或者--之前,有其它线程进来运行也可以解释这种情况了。事实上,比如"System.out.println(Thread.currentThread().getName()+"---"+tickets--); "虽然只是一句代码,其实也是分成了多步的?

作者: 暮雨    时间: 2013-9-26 10:07
刘渝灵 发表于 2013-9-26 08:42
你的意思就是说tickets--不是一步完成的操作,在tickets自减之前,多条线程可能已经输出tickets了?其实 ...

不是程序把这句分成了几份,是因为每个线程运行都需要cpu,他的cpu时间片用完就必须进入等待队列,把cpu资源让给其他线程,等待下次时间片的分配。得到时间片才能继续运行。

作者: 残影    时间: 2013-9-26 11:17
这个就是多线程不安全问题,毕老师视频里面有讲过的,上面的同学说的都是对,要不您再看看视频,老师有点到过,虽然老师没有运行出。




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