黑马程序员技术交流社区

标题: 多线程安全问题,标题一定要长-------------------------------- [打印本页]

作者: 徐传任    时间: 2012-10-10 12:59
标题: 多线程安全问题,标题一定要长--------------------------------
  1. public class TicketDemo {
  2.         public static void main(String[] args) {
  3.                 Ticket t = new Ticket();
  4.                 Thread t0 = new Thread(t);
  5.                 Thread t1 = new Thread(t);
  6.                 t0.setName("窗口一:");
  7.                 t1.setName("窗口二:");
  8.                 t0.start();
  9.                 t1.start();
  10.         }
  11. }

  12. class Ticket implements Runnable {
  13.         private int num = 100;
  14.         public void run() {
  15.                 while(true) {
  16.                         try {
  17.                                 sell();
  18.                         } catch (InterruptedException e) {
  19.                                 e.printStackTrace();
  20.                         }
  21.                 }
  22.         }
  23.        
  24.         public void sell() throws InterruptedException {
  25.                 if(num>0) {
  26.                         Thread.sleep(10);
  27.                         System.out.println(Thread.currentThread().getName()+"卖票啦,卖票啦"+num--);
  28.                 }
  29.         }
  30. }
复制代码
我知道会产生安全问题,会出现0号票,不安全。但是我在运行的时候突然发现有两个同号的票,这个问题是怎么产生的。
窗口一卖票啦,卖票啦80
窗口二卖票啦,卖票啦80
窗口二卖票啦,卖票啦79
窗口一卖票啦,卖票啦78
窗口二卖票啦,卖票啦77
窗口一卖票啦,卖票啦76
窗口一卖票啦,卖票啦75
为什么会出现两个80号票呢?
是 num--在运行后给num赋值期间出现的问题吗?
如果分开写
System.out.println(Thread.currentThread().getName()+"卖票啦,卖票啦"+num);
num--;这样我知道是两条语句出现问题,
但是System.out.println(Thread.currentThread().getName()+"卖票啦,卖票啦"+num--);这不是一条语句吗为什么还会出现问题。
具体System.out.println(Thread.currentThread().getName()+"卖票啦,卖票啦"+num--);这条语句在多线程期间是怎么出现同号票安全问题的原因的,麻烦给解释下

作者: 王震阳老师    时间: 2012-10-10 13:08
你的程序没有加线程同步呀,有时候可能看不到问题,但有时候会出问题。加上线程同步就可以了。、


作者: 徐传任    时间: 2012-10-10 13:08
弄不大明白这个打印线程+num--,在内存中到底是具体怎么执行的
作者: 王震阳老师    时间: 2012-10-10 13:17
本帖最后由 王震阳 于 2012-10-10 13:28 编辑
  1. public class TicketDemo {
  2.         public static void main(String[] args) {
  3.                 Ticket t = new Ticket();
  4.                 Thread t0 = new Thread(t);
  5.                 Thread t1 = new Thread(t);
  6.                 t0.setName("窗口一11111111111111:");
  7.                 t1.setName("窗口二222:");
  8.                 t0.start();
  9.                 t1.start();
  10.         }
  11. }

  12. class Ticket implements Runnable {
  13.         private int num = 1000;//为了测试效果,将票数改为1000张
  14.                 Object obj=new Object();//定义一个object对象
  15.         public void run() {
  16.                 while(true) {
  17.                                         synchronized(obj)//在这个地方加了一个同步锁
  18.                                         {
  19.                         try {
  20.                                 sell();
  21.                         } catch (InterruptedException e) {
  22.                                 e.printStackTrace();
  23.                         }
  24.                                         }
  25.                 }
  26.         }
  27.         
  28.         public void sell() throws InterruptedException {
  29.                 if(num>0) {
  30.                         Thread.sleep(10);
  31.                         System.out.println(Thread.currentThread().getName()+"卖票啦,卖票啦"+num--);
  32.                 }
  33.         }
  34. }


  35. 运行截图:

复制代码

作者: 徐传任    时间: 2012-10-10 13:17
王震阳 发表于 2012-10-10 13:08
你的程序没有加线程同步呀,有时候可能看不到问题,但有时候会出问题。加上线程同步就可以了。、

...

我知道可能出现问题,但是分析不出来为什么会出现同一张票的问题,郁闷
作者: 王震阳老师    时间: 2012-10-10 13:26
本帖最后由 王震阳 于 2012-10-10 13:32 编辑

[quote]徐传任 发表于 2012-10-10 13:17 http://edu.csdn.net/main/feature/bxd_25.shtml

你把第十一天的下载下来就知道了。

你的问题的主要解释为:因为cpu的执行不是连续的,每个线程都分配了执行时间,交换进行的,当第一个进程判断为true是,可能cpu就改换另一个进程了,
导致线程而也判断为true,这样两个线程都出售了同一张票。因此我们要解决这个方法就是,当线程1进入判断是,我们加一个锁,当线程1出来时再解锁,这样会降低系统运行的效率,
但是提高了安全性,或者说,类似于售票的问题,必须有线程安全方面的考虑。





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