黑马程序员技术交流社区

标题: 关于多线程共享资源的问题,望大神指点迷津啊!! [打印本页]

作者: kakaxi117    时间: 2014-2-24 21:56
标题: 关于多线程共享资源的问题,望大神指点迷津啊!!
关于多线程模拟售票的一个小程序。同时开四个线程相当于售票时的四个售票窗口,共同出售20张票,代码编写如下:
class Ticket implements Runnable//定义一个实现Runnable借口的类,包含多线程要实现的代码
{
private int ticket=20;
public void run()
{
  while(true)
  {
   if (ticket>0)
   {
    System.out.println(Thread.currentThread().getName()+"sold ticket:"+ticket);
    ticket--;
   }
   
  }
}
}
class  TicketThread
{
public static void main(String[] args)
{
  Ticket t = new Ticket();//创建并实例化一个Ticket类

//创建四个线程
  new Thread(t).start();  
  new Thread(t).start();
  new Thread(t).start();
  new Thread(t).start();
}
}
运行结果如图所示出现了多个编号为20的票,当把ticket--与输出语句System.out.println(Thread.currentThread().getName()+"sold ticket:"+ticket);合并为System.out.println(Thread.currentThread().getName()+"sold ticket:"+ticket--);后就不再出现了,为什么会出现这种情况;另外有的书上说类继承Thread并不能实现资源共享,为什么还有用继承Thread来实现多线程的方法呢?

QQ图片20140224215301.jpg (98.01 KB, 下载次数: 15)

QQ图片20140224215301.jpg

作者: xietansheng    时间: 2014-2-24 22:38
本帖最后由 xietansheng 于 2014-2-24 22:41 编辑

线程安全问题,多个线程访问同一数据时,需要加同步锁
而且你的打印结果也不符合逻辑,打印顺序应该是20>>19>>18...>>1

把ticket--与输出语句System.out.println(Thread.currentThread().getName()+"sold ticket:"+ticket);合并为System.out.println(Thread.currentThread().getName()+"sold ticket:"+ticket--);也会出问题的,只是概率问题。

还有,在while循环中出现了死循环,线程一直在执行,不能跳出。

优化建议如下:

  1. /**
  2. * 定义一个实现Runnable借口的类,包含多线程要实现的代码
  3. */
  4. class Ticket implements Runnable
  5. {
  6.         private int ticket = 20;

  7.         public void run()
  8.         {
  9.                 while (true)
  10.                 {
  11.                         synchronized (this)        //同步锁,防止多个线程访问同一数据时出错
  12.                         {
  13.                                 if (ticket > 0)
  14.                                 {
  15.                                         System.out.println(Thread.currentThread().getName() + " sold ticket: " + ticket);
  16.                                         ticket--;
  17.                                 }
  18.                                 else
  19.                                 {
  20.                                         break;       //票已卖完
  21.                                 }
  22.                         }
  23.                 }
  24.         }
  25. }

  26. public class TicketThread
  27. {
  28.         public static void main(String[] args)
  29.         {
  30.                 Ticket t = new Ticket();// 创建并实例化一个Ticket类

  31.                 // 创建四个线程
  32.                 new Thread(t).start();
  33.                 new Thread(t).start();
  34.                 new Thread(t).start();
  35.                 new Thread(t).start();
  36.         }
  37. }
复制代码

作者: jkenny    时间: 2014-2-24 23:18
没错就是楼上说的线程安全问题。
System.out.println(Thread.currentThread().getName()+"sold ticket:"+ticket);
ticket--;//不合并的
-------------------
System.out.println(Thread.currentThread().getName()+"sold ticket:"+ticket--); //合并的
-------------------
其实这2个都会出现你图上的问题(相同的票),只不过“合并的”出现的几率比较小。
因为:不合并的,是2条语句,你创建的4个线程,是轮流执行程序的。
如:第1个线程执行了(不合并的)第一条语句,“那么也就是打印了一个20”然后就停止了,还没将ticket--
然后第2个线程就执行了,因为第1个线程没执行ticket--就停止了所以ticket还是20。所以第2个线程就又打印了一个20。  第3,第4个线程的情况也是这样。所以就出现了你图上的情况。
那么如果合并的话就好多了。
如:第1个线程执行了一条语句,打印了20并ticket--。然后停止了。之后第2个线程就打印19了,因为前面线程1有执行ticket--。第3,第4线程也是这样。
所以就像你说的合并之后就没有图上的问题了。但是其实还是会出现图上的问题,只不过几率比较小了。
如:那条(合并的)语句没执行完,就停止了,然后开始第2 条线程。
所以这种程序需要加:synchronized 如楼上的。

还有 while (true) 感觉不好,写成for把,如:

  1.         private int ticket = 20;
  2.         public void run(){
  3.                 for(;ticket>0;ticket --){
  4.                         System.out.println(ticket);
  5.                 }
  6.         }
复制代码

作者: .......    时间: 2014-2-25 12:02
加上同步锁
作者: kakaxi117    时间: 2014-2-25 22:42
xietansheng 发表于 2014-2-24 22:38
线程安全问题,多个线程访问同一数据时,需要加同步锁
而且你的打印结果也不符合逻辑,打印顺序应该是20>>1 ...

多谢指导!:D
作者: kakaxi117    时间: 2014-2-25 22:43
jkenny 发表于 2014-2-24 23:18
没错就是楼上说的线程安全问题。
System.out.println(Thread.currentThread().getName()+"sold ticket:"+ti ...

受教了。
作者: kakaxi117    时间: 2014-2-25 22:45
jkenny 发表于 2014-2-24 23:18
没错就是楼上说的线程安全问题。
System.out.println(Thread.currentThread().getName()+"sold ticket:"+ti ...

受教了,谢谢解释!




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