A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 王桂丽 中级黑马   /  2012-9-1 20:42  /  2558 人查看  /  11 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

//定义TicketSealCenter类
class TicketSealCenter
{      
        public static void main(String[] args)
  {
                // 生产 一千张高铁票 送到售票亭
                Ticket t = new Ticket(1000);
                //启动5个线程
                SealWindow sw1 = new SealWindow("售票窗口一",t);
                SealWindow sw2 = new SealWindow("售票窗口二",t);
                SealWindow sw3 = new SealWindow("售票窗口三",t);
                SealWindow sw4 = new SealWindow("售票窗口四",t);
                SealWindow sw5 = new SealWindow("售票窗口五",t);
                new Thread(sw1).start();
                new Thread(sw2).start();
                new Thread(sw3).start();
                new Thread(sw4).start();
                new Thread(sw5).start();                              
        }
}
//定义SealWindow类
class SealWindow implements Runnable
{
  //设置窗口属性
        private String name;
        private Ticket t;
        public SealWindow(String name,Ticket t)
  {
                this.name = name;
                this.t=t;
        }
  //覆盖run方法
        public void run()
  {   
   synchronized(t)
   {
   boolean b=true;
   while(b)
   {
   //如果还有票,打印所售票的窗口和剩余的票数
   if (t.sealTicket())
   {
    System.out.println(name+"·······售出一张,还剩余:"+t.getNum());
   }
   //如果没有,则打印“票已售完”
   else
   {
    System.out.println("票已售完");
    b=false;
   }
   
   }
   }
        }
}
//定义Tickt类
class Ticket
{
  //设置属性所需售票数num
        private  int num;
        public Ticket(int num)
  {      
                this.num = num;
        }      
         //返回剩余票数      
        public int getNum()
  {               
                return num;
        }        
         //功能:卖票,卖出去了返回 true,没卖出去返回false;
        public boolean sealTicket()
  {
                if(num<=0)
                {
                        return false;
                }
                else
                {
                        num--;
                        return true;
                }
  }
}



疑问:请问代码哪里出了问题,为什么线程都只运行一次就停止了?

11 个回复

倒序浏览
你的程序是这样运行的:
     当一个线程进了run方法中获得锁独线程运行里面的代码,但是里面个个while判断时,就直接在里面循环 ,直到把票买完了 他才会把 b 改为false ,打印票已售完退出whlie,下一个线程才进去,发现没票了就打印票已售完退出while  释放锁,,接着其他三个也一样,
    问题出现在同步代码块因该放在  while 循环了里面,这样就可以。
     值得说的是,可能只需要两到三个线程进来就把票卖完了,是因为cpu处理的时间片问题。在一个时间片里,可能一个线程就买了几百张票了


不明白可以继续追问
回复 使用道具 举报
打错了几个字 不好意思,  {:soso_e113:}
回复 使用道具 举报
楼上的正解,关键是需要明确的知道哪些地方该被同步,楼主把覆盖run方法的代码改成下面的就可以了
        public void run()
   {   
    boolean b=true;
    while(b)
    {
    synchronized(t)
       {
    //如果还有票,打印所售票的窗口和剩余的票数
    if (t.sealTicket())
    {
     System.out.println(name+"·······售出一张,还剩余:"+t.getNum());
    }
    //如果没有,则打印“票已售完”
    else
    {
     System.out.println("票已售完");
     b=false;
    }
   
    }
    }
         }
}
回复 使用道具 举报
  1. class TicketSealCenter
  2. {
  3.         public static void main(String[] args)
  4.         {
  5.                 // 生产 一千张高铁票 送到售票亭
  6.                 Ticket t = new Ticket(1000);
  7.                 //启动5个线程
  8.                 SealWindow sw1 = new SealWindow("售票窗口一",t);
  9.                 SealWindow sw2 = new SealWindow("售票窗口二",t);
  10.                 SealWindow sw3 = new SealWindow("售票窗口三",t);
  11.                 SealWindow sw4 = new SealWindow("售票窗口四",t);
  12.                 SealWindow sw5 = new SealWindow("售票窗口五",t);
  13.                 new Thread(sw1).start();
  14.                 new Thread(sw2).start();
  15.                 new Thread(sw3).start();
  16.                 new Thread(sw4).start();
  17.                 new Thread(sw5).start();                              
  18.         }
  19. }
  20. //定义SealWindow类
  21. class SealWindow implements Runnable
  22. {
  23.         //设置窗口属性
  24.         private String name;
  25.         private Ticket t;
  26.         public SealWindow(String name,Ticket t)
  27.         {
  28.                 this.name = name;
  29.                 this.t=t;
  30.         }
  31.         //覆盖run方法
  32.         public void run()
  33.         {   
  34.                 boolean b=true;
  35.                 while(b)
  36.                 {
  37.                 //如果还有票,打印所售票的窗口和剩余的票数
  38.                         if (t.sealTicket())
  39.                         {
  40.                                 System.out.println(name+"·······售出一张,还剩余:"+t.getNum());
  41.                         }
  42.                         //如果没有,则打印“票已售完”
  43.                         else
  44.                         {
  45.                                 System.out.println("票已售完");
  46.                                 b=false;
  47.                         }   
  48.                 }
  49.         }
  50. }
  51. //定义Tickt类
  52. class Ticket
  53. {
  54.         //设置属性所需售票数num
  55.         private  int num;
  56.         public Ticket(int num)
  57.         {      
  58.                 this.num = num;
  59.         }      
  60.         //返回剩余票数      
  61.         public int getNum()
  62.         {               
  63.                 return num;
  64.         }        
  65.         //功能:卖票,卖出去了返回 true,没卖出去返回false;
  66.         public synchronized boolean sealTicket()
  67.         {
  68.                 if(num<=0)
  69.                 {
  70.                         return false;
  71.                 }
  72.                 else
  73.                 {
  74.                         num--;
  75.                         return true;
  76.                 }
  77.         }
  78. }
复制代码
把synchronized放到  public boolean sealTicket() 这个方法处就可以了
回复 使用道具 举报
周兴华 发表于 2012-9-1 22:03
楼上的正解,关键是需要明确的知道哪些地方该被同步,楼主把覆盖run方法的代码改成下面的就可以了
         ...

怎么我将num改成10000,结果很奇怪,直接从3000多是第一张票,求解?
回复 使用道具 举报
黑马张涛 发表于 2012-9-1 23:08
怎么我将num改成10000,结果很奇怪,直接从3000多是第一张票,求解?

应该是打印结果超出了控制台能显示的行数,我在System.out.println(name+"·······售出一张,还剩余:"+t.getNum());的后面加了一个sleep(1000),运行程序后在控制台可以观察到票数是从10000开始卖的。
回复 使用道具 举报
周兴华 发表于 2012-9-1 23:19
应该是打印结果超出了控制台能显示的行数,我在System.out.println(name+"·······售出一张,还剩 ...

谢谢,好像和内存分配的运行空间有关,我将打印的字删了一些,可以从7000开始打印了(没加sleep)
回复 使用道具 举报
阳杰 发表于 2012-9-1 21:50
打错了几个字 不好意思,


//定义TicketSealCenter类
class TicketSealCenter
{      
        public static void main(String[] args)
  {
                // 生产 一千张高铁票 送到售票亭
                Ticket t = new Ticket(1000);
                //启动5个线程
                SealWindow sw1 = new SealWindow("售票窗口一",t);
                SealWindow sw2 = new SealWindow("售票窗口二",t);
                SealWindow sw3 = new SealWindow("售票窗口三",t);
                SealWindow sw4 = new SealWindow("售票窗口四",t);
                SealWindow sw5 = new SealWindow("售票窗口五",t);
                new Thread(sw1).start();
                new Thread(sw2).start();
                new Thread(sw3).start();
                new Thread(sw4).start();
                new Thread(sw5).start();                  
            
        }
}
//定义SealWindow类
class SealWindow implements Runnable
{
  //设置窗口属性
        private String name;
        private Ticket t;
        public SealWindow(String name,Ticket t)
  {
                this.name = name;
                this.t=t;
        }
  //覆盖run方法
        public void run()
  {   
   
   boolean b=true;
   while(b)
   {
    synchronized(t)
     {
      //如果还有票,打印所售票的窗口和剩余的票数
      if (t.sealTicket())
       {
        System.out.println(name+"·······售出一张,还剩余:"+t.getNum());
       }
      //如果没有,则打印“票已售完”
      else
       {
        System.out.println("票已售完");
        b=false;
       }
   
     }
   }
        }
}
//定义Tickt类
class Ticket
{
  //设置属性所需售票数num
        private  int num;
        public Ticket(int num)
  {      
                this.num = num;
        }      
         //返回剩余票数      
        public int getNum()
  {               
                return num;
        }        
         //功能:卖票,卖出去了返回 true,没卖出去返回false;
        public boolean sealTicket()
  {
                if(num<=0)
                {
                        return false;
                }
                else
                {
                        num--;
                        return true;
                }
  }
}



运行结果如图,还是不对啊

未命名2.jpg (24.63 KB, 下载次数: 28)

未命名2.jpg

点评

你的是对的,你仔细看,程序运行是绝对不是至于窗口一的,前面过了个把两个窗口的,因为屏幕显示不完 ,太长了, 你按照我开始说的去理解程序。  发表于 2012-9-1 23:50
回复 使用道具 举报
//定义TicketSealCenter类
class TicketSealCenter
{      
        public static void main(String[] args)
                {
                // 生产 一千张高铁票 送到售票亭
                Ticket t = new Ticket(1000);
                //启动5个线程
                SealWindow sw1 = new SealWindow("售票窗口一",t);
                SealWindow sw2 = new SealWindow("售票窗口二",t);
                SealWindow sw3 = new SealWindow("售票窗口三",t);
                SealWindow sw4 = new SealWindow("售票窗口四",t);
                SealWindow sw5 = new SealWindow("售票窗口五",t);
                new Thread(sw1).start();
                new Thread(sw2).start();
                new Thread(sw3).start();
                new Thread(sw4).start();
                new Thread(sw5).start();                              
        }
}
//定义SealWindow类
class SealWindow implements Runnable
{
  //设置窗口属性
        private String name;
        private Ticket t;
        public SealWindow(String name,Ticket t)
                {
                this.name = name;
                this.t=t;
        }
  //覆盖run方法
        public void run()
           {   
                          // synchronized(t)
                          // {
                                           boolean b=true;
                                           while(b)
                                           {
                                                  synchronized(t)
                                                  { //如果还有票,打印所售票的窗口和剩余的票数
                                                                   if (t.sealTicket())
                                                                   {
                                                                        System.out.println(name+"·······售出一张,还剩余:"+t.getNum());
                                                                   }
                                                                   //如果没有,则打印“票已售完”
                                                                   else
                                                                   {
                                                                        System.out.println("票已售完");
                                                                        b=false;
                                                                   }
                                                  }   
                                          }
                           //}
                }
}
//定义Tickt类
class Ticket
{
  //设置属性所需售票数num
        private  int num;
        public Ticket(int num)
                {      
                this.num = num;
        }      
         //返回剩余票数      
        public int getNum()
                {               
                return num;
        }        
         //功能:卖票,卖出去了返回 true,没卖出去返回false;
        public boolean sealTicket()
                {
                if(num<=0)
                {
                        return false;
                }
                else
                {
                        num--;
                        return true;
                }
                }
}

回复 使用道具 举报
此问题已解决
回复 使用道具 举报
王桂丽 发表于 2012-9-1 23:30
//定义TicketSealCenter类
class TicketSealCenter
{      

已经明白了~
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马