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

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© breeze 中级黑马   /  2013-5-29 13:34  /  1660 人查看  /  9 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

本帖最后由 breeze 于 2013-5-30 16:50 编辑

程序是将卖票的票, 卖票窗口, 售票中心 分别定义成类来实现的, 但是我的运行结果的中前四个售出的票出现了相同的号码, 请大家帮我看一下是哪里出了问题
  1. package com.itheima.exam;

  2. public class Test19 {
  3.         int x = 1;
  4.         public static void main(String[] args) {
  5.                 new TicketSealCenter();
  6.         
  7.         }
  8. }
  9. //定义票的类
  10. class Ticket {
  11.         int ticketNumber = 1;
  12.         
  13.         public int getTiket() {
  14.                 return ticketNumber++;
  15.         }
  16. }
  17. //定义卖票窗口的类
  18. class SealWindow {
  19.         Ticket ticket = new Ticket();
  20.         
  21.         public synchronized int sealTicket() {
  22.                 return ticket.getTiket();
  23.         }
  24. }
  25. //定义售票中心的类
  26. class TicketSealCenter {
  27.         
  28.         int totalTicket = 100;
  29.         SealWindow sealWindow = new SealWindow();
  30.         int totalWindow = 4;
  31.         int windowId = 1;
  32.         int ticket = 1;
  33.         
  34.         public TicketSealCenter() {
  35.                 while (windowId <= totalWindow) {
  36.                         //开启线程
  37.                         new Thread() {
  38.                                 //得到窗口ID
  39.                                 int windowId = TicketSealCenter.this.windowId++;
  40.                                 public void run() {
  41.                                         try {
  42.                                                 Thread.sleep(1);
  43.                                         } catch (InterruptedException e) {
  44.                                                 e.printStackTrace();
  45.                                         }
  46.                                         while (true) {
  47.                                                 ticket = sealWindow.sealTicket();
  48.                                                 if (ticket > totalTicket)
  49.                                                         System.exit(0);
  50.                                                 System.out.println(windowId + "窗口卖了" + ticket + "号票");
  51.                                         }
  52.                                 }
  53.                         }.start();
  54.                 }
  55.         }
  56. }
复制代码

评分

参与人数 1技术分 +1 收起 理由
曹睿翔 + 1 为了增加效果,多打印些信息,入学测试分才.

查看全部评分

9 个回复

倒序浏览
本帖最后由 Neverbelazy 于 2013-5-29 14:07 编辑

打印也需要是同步的操作, 否则, 假设

窗口1sealTicket(....sellTicket)买票--->挂起;  ticket=10;
窗口2sealTicket——>挂起; ticket=11;
窗口3sealTicket——>挂起; ticket=12;
窗口4sealTicket——>挂起;ticket=13;
此时, 窗口1-4依次继续向下执行, 每个线程都执行到刚好打印结束才切换到下一个线程
打印的票就是:
1窗口13;
2窗口13;
3窗口13;
4窗口13;

程序中windowID最好是sealWindow的成员, 这样更合理, 而且会对之后修改同步代码很方便

请参考

评分

参与人数 1技术分 +1 收起 理由
曹睿翔 + 1 神马都是浮云

查看全部评分

回复 使用道具 举报
本帖最后由 breeze 于 2013-5-29 16:23 编辑
Neverbelazy 发表于 2013-5-29 14:05
打印也需要是同步的操作, 否则, 假设

窗口1sealTicket(....sellTicket)买票--->挂起;  ticket=10;

这样岂不是容易造成死锁, 获取的 ticket是在线程内部的run方法定义的, 它怎么会改变呢
回复 使用道具 举报
本帖最后由 Neverbelazy 于 2013-5-29 16:58 编辑
breeze 发表于 2013-5-29 16:22
这样岂不是容易造成死锁, 获取的 ticket是在线程内部的run方法定义的, 它怎么会改变呢 ...

1. ticket是你用sealWindow.sealTicket得到的 除了这一步是同步外, 其他部分都没有同步
2. 你指的死锁应该是 嵌套了 synchronized(){ sealTicket()} 但是, 如果 synchronized(){}这个整体就不用再 将 sealTicket 标记为synchronized了
3. 另外,
while(true){
   //我的经验是,这行最好加一句Thread.sleep(xxx); 否则一个线程抢到执行权不放, 自己无限循环
   synchromized(xxx){
   // 同步代码
   }
}

回复 使用道具 举报
我在控制台测试一下你的代码没有问题呀,你出现问题是不是因为运行环境的问题,下面是我运行结果截图

1.png (4.29 KB, 下载次数: 0)

1.png
回复 使用道具 举报
吴刚—heima 发表于 2013-5-29 16:52
我在控制台测试一下你的代码没有问题呀,你出现问题是不是因为运行环境的问题,下面是我运行结果截图 ...

......
......
while(true){
ticket=sealWindow.sealTicket();
//在这个之后加一句 try{Thread.sleep(10)}catch(Exception e){};
....
....
}

之后就能看到重复票了
回复 使用道具 举报
本帖最后由 Neverbelazy 于 2013-5-29 16:56 编辑
吴刚—heima 发表于 2013-5-29 16:52
我在控制台测试一下你的代码没有问题呀,你出现问题是不是因为运行环境的问题,下面是我运行结果截图 ...

......
......
while(true){
ticket=sealWindow.sealTicket();
//在这个之后加一句 try{Thread.sleep(10);}catch(Exception e){};
....
....
}

之后就能看到重复票了
回复 使用道具 举报
  //开启线程
                        new Thread() {
                                //得到窗口ID
                                int windowId = TicketSealCenter.this.windowId++;
                                public void run() {
                                        try {
                                                Thread.sleep(1);
                                        } catch (InterruptedException e) {
                                                e.printStackTrace();
                                        }
                                        while (true) {
                                                ticket = sealWindow.sealTicket();
                                                if (ticket > totalTicket)
                                                        System.exit(0);
                                                System.out.println(windowId + "窗口卖了" + ticket + "号票");
                                        }
                                }
                        }.start();

如果还是不能得到我这个结果,我建议你将这一段加上synchronized 监听一下,你这段代码会产生四个线程,但是这四个线程却没有加上监听,所以还是可能会产生线程异步的情况。
回复 使用道具 举报
本帖最后由 Neverbelazy 于 2013-5-29 17:12 编辑

Breeze, 我知道你的意思了,

在你这个程序中只要 变成 int ticket=sealWindow.sealTicket(); 变成局部变量程序就可以正常运行了,
之前,你里面的ticket是一直在重复为成员的ticket赋值;而成员ticket是这几个Thread共有的

更好的写法是 换个名称 int piao=sealWindow.sealTicket(); 这样就没有歧义了

回复 使用道具 举报
Neverbelazy 发表于 2013-5-29 17:07
Breeze, 我知道你的意思了,

在你这个程序中只要 变成 int ticket=sealWindow.sealTicket(); 变成局部变 ...

就是这个原因, 谢谢你可以仔细看我的代码, 能不能留个你的联系方式, 有问题可以随时讨论

点评

嘿...我就是laughing啊...qq已加. 博客地址分享给你吧, 互相学习http://blog.csdn.net/wanglonghao88/article/category/1418047  发表于 2013-5-30 17:12
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马