黑马程序员技术交流社区

标题: 关于多线程卖票的问题 [打印本页]

作者: (づ ̄_3 ̄)づ    时间: 2014-11-17 10:54
标题: 关于多线程卖票的问题
  1. public class Test {

  2.         public static void main(String[] args) {
  3.                 Tick tick = new Tick();
  4.                 new Thread(tick).start();
  5.                 new Thread(tick).start();
  6.                 new Thread(tick).start();
  7.         }
  8. }

  9. class Tick implements Runnable{
  10.         private int tick=300;
  11.         public void run(){
  12.                 while(true){
  13.                         if(tick>0){
  14.                                 try {
  15.                                         Thread.sleep(1);
  16.                                 } catch (InterruptedException e) {
  17.                                         e.printStackTrace();
  18.                                 }
  19.                                 System.out.println(Thread.currentThread().getName()+"  "+tick--);
  20.                         }
  21.                 }
  22.         }
  23. }
复制代码
我复习了下多线程的那块,但是卖票的那个程序,不写同步的话结尾出现0或者负数,这个我知道怎么解释,但是出现多个线程卖同一张票,应该怎么解释原因呢?

QQ截图20141117105231.png (6.84 KB, 下载次数: 16)

运行结果

运行结果

作者: 我是紫色紫色    时间: 2014-11-17 11:11
这个很简单啊,你想啊,同步就是加把锁,一个线程进来了以后就会加锁,其他线程进不来,只有等它执行完了释放了锁对象其他线程才能抢到执行权,如果你不加锁,当一个线程进来,获得执行权,当它睡眠的时间内,可能其他的线程进来了获得了同样的执行权,两个线程执行同一个对象,然后打印输出,所以这个时候就会出现这种两个线程打印同一张票的情况!
作者: (づ ̄_3 ̄)づ    时间: 2014-11-17 11:18
我是紫色紫色 发表于 2014-11-17 11:11
这个很简单啊,你想啊,同步就是加把锁,一个线程进来了以后就会加锁,其他线程进不来,只有等它执行完了释 ...

总觉得你说的这个没在点子上啊,这些我都懂,但是执行输出语句肯定也是某个线程先执行,输出以后tick应该就减了,下一个线程输出的时候应该是减过以后的吧
作者: 我是紫色紫色    时间: 2014-11-17 11:25
(づ ̄_3 ̄)づ 发表于 2014-11-17 11:18
总觉得你说的这个没在点子上啊,这些我都懂,但是执行输出语句肯定也是某个线程先执行,输出以后tick应该 ...

就是还没执行--操作的时候就被另一个线程抢过去啦!所以这个时候值还没来得及减1,懂了么?所以才会出现两个打印同一张票!你要知道cpu切换速度很快的,不是一定会减1后才执行下一个线程操作!这就是要加锁的原因!
作者: 关军波    时间: 2014-11-17 12:16
学习了!!!!!
作者: (づ ̄_3 ̄)づ    时间: 2014-11-17 12:43
我是紫色紫色 发表于 2014-11-17 11:25
就是还没执行--操作的时候就被另一个线程抢过去啦!所以这个时候值还没来得及减1,懂了么?所以才会出现 ...

就算切换很快,也肯定是会执行完一条语句的,--包含在那条输出语句里面了,要不就是没减也没输出就被抢走了执行权,要不就是减了以后输出
作者: 我是紫色紫色    时间: 2014-11-17 13:06
(づ ̄_3 ̄)づ 发表于 2014-11-17 12:43
就算切换很快,也肯定是会执行完一条语句的,--包含在那条输出语句里面了,要不就是没减也没输出就被抢走 ...

可能老毕没讲清楚所以你也不理解,我看的是另外一个的视频,很清楚
作者: a8851625    时间: 2014-11-17 14:18
因为多个线程都在抢资源啊,a线程拿到了这张票,还没卖出去,b线程冲进来也拿到了这张票,于是就两个人一起卖了出去
作者: 米和饭    时间: 2014-11-18 10:27
线程a在卖票1的时候,因为你没加锁,所以线程b不知道线程a正在卖票1,所以线程b也可以卖票1,这样就出现了多个线程同卖一张票的情况了。。。。。

作者: 李运岚    时间: 2014-11-18 11:11
两个纯种在循环中,刚开始进来的时候tick是同一个数,一个线程减1了之后,第二个线程并不是去调用第一个线程减1之后的tick
作者: FlyFish    时间: 2014-11-18 12:04
num=100;
A线程挂这,A.num=100,B线程也到这,B.num也是100
s.o.p(--num);
A执行输出=99,B执行输出也是99。
作者: 奋斗的蜗牛ksd    时间: 2014-11-18 13:58
兄弟,我觉得你是没看线程安全的那部分把,

线程是并发执行的,首先你把300张票共享给 三个线程,即三个线程对300票同时处理
通俗点说是  线程123都有对 同一张票的执行权,而且共享 票的资源是递减的 这个特性
当线程2在执行票171的时候,执行权---执行输出的权利-----被sleep 延迟了----这个时候执行权放出来了,线程1抢到了,----因为线程123共享  剩下的第171个票,-----这个时候没有线程抢到171执行权---线程1先打印了这个结果----线程2醒了,又打印了-

---------------错误的原因就是有先12都进入了if语句中----所以要再if语句外部加同步

加上同步解决安全问题的原因-----是因为 只允许让 一个拿着171票的线程执行,就算它sleep了,171的执行权也还在这个睡着的线程手里面!,等它sleep结束,继续执行,执行完毕,锁才会开。

这种解释方法也可以解释  0  和-1票的情况,因为 当1锁住的时候,及时其他线程拿到 0 和-1 的票,因为要等到1执行完毕,所以进来判断的时候,也不会打印滴!
--------个人理解--------同步的作用是排队----而不是为了避免 0和-1 -2的错误!
作者: (づ ̄_3 ̄)づ    时间: 2014-11-18 14:20
奋斗的蜗牛ksd 发表于 2014-11-18 13:58
兄弟,我觉得你是没看线程安全的那部分把,

线程是并发执行的,首先你把300张票共享给 三个线程,即三个线 ...

线程安全我看了的,0、-1的问题我也懂,照你这说法线程1打印了结果,输出语句中把tick--了,到线程2醒了的时候,打印的应该是170才对啊
作者: 奋斗的蜗牛ksd    时间: 2014-11-18 14:23
等线程2醒了,但是只要它进入if语句了  它的票还是171票!
作者: Eagle    时间: 2014-11-18 14:33
(づ ̄_3 ̄)づ 发表于 2014-11-18 14:20
线程安全我看了的,0、-1的问题我也懂,照你这说法线程1打印了结果,输出语句中把tick--了,到线程2醒了 ...

应该是线程的用来减一的tick是用的进入判断的时候的那个tick吧。
线程一还没有减一就挂了。
然后线程二进来时还是171对吧。因为线程一还没有进行减一操作。
所以两个线程减一时用的tick都是171.就是在if判断的时候的tick。
不知道是不是这样。楼主可自行理解。呵呵

作者: 郑飞    时间: 2014-11-18 15:27
我的理解是这样的
线程1 在判断完tick>0的时候拿到 tick是171 然后睡去了
线程2 进来判断tick>0后 拿到的也是171
打印出来的就都是171了
楼主的困惑应该是 为什么线程1醒来执行了--后 tick值不影响 线程2手中的tick值
这应该和内存中方法跟变量的加载有关系 可以看下老毕的内存分析 我也忘差不多了{:3_48:}




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