黑马程序员技术交流社区

标题: 求解下售票是哪个地方出问题了 [打印本页]

作者: 张云杰    时间: 2013-6-16 22:43
标题: 求解下售票是哪个地方出问题了
/*
* 编写三各类Ticket、SealWindow、TicketSealCenter分别代表票信息、售票窗口、售票中心。
* 售票中心分配一定数量的票,由若干个售票窗口进行出售,利用你所学的线程知识来模拟此售票过程
*/
public class TicketSealCenter {
/*
  * 售票中心 分配售票窗口 和总票数
  */

public static void main(String[] args) {
  Ticket ticket = new Ticket(100);//初始化票数
  
  /*
   * 4个售票窗口
   */
  SealWindow sw = new SealWindow("售票窗口1",ticket);
  SealWindow sw1 = new SealWindow("售票窗口2",ticket);
  SealWindow sw2 = new SealWindow("售票窗口3",ticket);
  SealWindow sw3 = new SealWindow("售票窗口4",ticket);
  
  new Thread(sw).start();
  new Thread(sw1).start();
  new Thread(sw2).start();
  new Thread(sw3).start();
  
  
  
}
}
class SealWindow implements Runnable{
/*
  * 买票,线程开启后 这里开始做事,那么这里就必须要实现RUNNERBLE接口
  */
private String selwindowname;
private Ticket ticket;
private boolean flag=true;
/*
  * 初始化窗口 给了票的数量 以及 窗口名字
  */
public SealWindow(String selwindowname, Ticket ticket) {
  this.selwindowname = selwindowname;
  this.ticket = ticket;
}
@Override
public void run() {
  sl();
}
private synchronized void sl(){
  while(flag){
   System.out.println(selwindowname+"...."+ticket.sellTicket());
   if(ticket.sellTicket()==0){
    flag = false;
    try {
     Thread.sleep(100);
    } catch (InterruptedException e) {
    }
   }
  }
}
}
class Ticket {
private int ticketnum;
public Ticket(int ticketnum) {
  this.ticketnum = ticketnum;
}
/*
  * 卖票
  */

public synchronized int sellTicket(){
  if(ticketnum>0){
   return  ticketnum--;
  }else{
   return 0;
  }
}

}

应该是线程哪里的不同步造成的,  用了synchronized , 照理来说 线程1进去了 线程2不会进才对.. 但是 ticketnum-- 还是被4条线程同时做了计算, 不知道是什么情况 各种求解

作者: 张云杰    时间: 2013-6-16 23:06

作者: 张云杰    时间: 2013-6-16 23:07
遇0判断已经可以了.. 就是计算的时候 一直被线程给干了.. 求解

作者: 王磊    时间: 2013-6-16 23:42
SealWindow sw = new SealWindow("售票窗口1",ticket);
SealWindow sw1 = new SealWindow("售票窗口2",ticket);
SealWindow sw2 = new SealWindow("售票窗口3",ticket);
SealWindow sw3 = new SealWindow("售票窗口4",ticket);
楼主创建的貌似是4个线程参数的对象,这样再通过run方法调用sl()方法时,虽然加同步,但是同步函数的锁是this,也就是当前调用方法的对象,4个线程对应了4把锁,相当于各自都在用自己的锁去操作共有数据ticket,所以循环主体是分别被4个线程一起执行的,而没起到同步的作用。
作者: 王磊    时间: 2013-6-16 23:49
再补充一下,把楼主的程序中的同步函数,改写成同步代码块的形式,用一个Object类对象作为锁的话,楼主应该就能看出问题了。就是4个对象各用一个锁和4个对象公用一个锁的区别。
作者: 张云杰    时间: 2013-6-16 23:55
王磊 发表于 2013-6-16 23:42
SealWindow sw = new SealWindow("售票窗口1",ticket);
SealWindow sw1 = new SealWindow("售票窗口2",tick ...

请问兄弟 那我应该怎么加? 是在while内 加个sybchronized(obj)   ???
作者: 张云杰    时间: 2013-6-16 23:57
private void sl(){
                synchronized (obj) {
                int i = ticket.sellTicket();
                System.out.println(selwindowname + "...." + i);
                if (ticket.sellTicket() == 0) {
                        flag = false;
                        try {
                                Thread.sleep(1000);
                        } catch (InterruptedException e) {
                        }
                }
                }
        }






        Object obj = new Object();
        public int sellTicket() {
                synchronized (obj) {
                if (this.ticketnum > 0) {
                        return ticketnum--;
                } else {
                        return 0;
                }
                }
        }




2个分别这样加了 没效果
作者: 张云杰    时间: 2013-6-16 23:58
王磊 发表于 2013-6-16 23:49
再补充一下,把楼主的程序中的同步函数,改写成同步代码块的形式,用一个Object类对象作为锁的话,楼主应该 ...

我现在纠结的是 共享数据加了 结果不同步 让我头疼.. 你说的方法 我都试过了 无效啊
作者: 王磊    时间: 2013-6-17 00:11
楼主这样试试,改成同步代码块,并且SealWindow类中创建这样一个Object对象private static final Object obj = new Object();

就是保证锁的唯一性。再试试
作者: 王磊    时间: 2013-6-17 00:20
貌似不用加final也可以。我这么做只是想给楼主说明一下问题,因为这么写完以后,虽然可以保证同步时锁的唯一性,但是其他线程也同样进了sl()方法,当有执行权的线程把循环运行结束后,其他线程也会在判断一次循环。就相当于把0号票又卖了3次。
所以,这个代码最优化的方法,应该是创建线程参数对象时,只创建一个,这样就能保证4个线程操作同一个对象,而不是4个线程去操作4个对象中的相同数据。
如果楼主想给线程定义名字的话,可以在创建线程对象时定义,而不是在创建线程参数对象时定义。
作者: 张云杰    时间: 2013-6-17 00:40
王磊 发表于 2013-6-17 00:20
貌似不用加final也可以。我这么做只是想给楼主说明一下问题,因为这么写完以后,虽然可以保证同步时锁的唯 ...

哦 其实这里对象不能加锁.. 如果加了一样的锁 有可能线程1执行2次 打印2次, 线程2 就执行不到了.. 就没的打印, 然后下一轮的线程又开始抢, 所以这里只能用 函数同步
作者: 王磊    时间: 2013-6-17 00:59
张云杰 发表于 2013-6-17 00:40
哦 其实这里对象不能加锁.. 如果加了一样的锁 有可能线程1执行2次 打印2次, 线程2 就执行不到了.. 就没的 ...

其实就像老师讲的那样,同步前提就是要保证使用的是同一个锁,如果每个线程都有自己的锁,那么虽然都是操作共有的数据,但是还是没有一个互相约束的作用,就等于各自只完成了自己的同步,就算在同步过程中失去执行权,其他线程也只要判断自己的锁就可以了。个人感觉楼主的这个代码如果用同步函数的话,还是不能保证同步。因为都用了this做锁,而创建了4个对象,就相当于每个线程都有了自己的this,这样就是4把锁,不能对共有数据同步了。
作者: 张云杰    时间: 2013-6-18 22:29
版主可在???  改成已解决,  以上代码就没有错,, 这样做就行了.. 至于同步已经有同步了.. 就按照上面做的, 我刚又重新看了一遍毕老师的卖票,  老毕说上面的现象是因为CPU造成,  也感谢下王磊兄, 这题加synchronized 跟加对象锁是一样的, 有同步, 关键得看线程是同步在哪个地方..
作者: 张云杰    时间: 2013-6-18 22:30
说错了.. 这个是错的.. 我刚自己又重新做了一遍没错... 这个代码有误.. 知道原因不.. 呵呵, 是因为 我用这个方法又判断了一次0所造成的, 害我纠结了一个晚上 + 一个凌晨 突然顿悟了
作者: 张云杰    时间: 2013-6-18 22:48
粗心害死人啊... 兄弟们 千万别粗心啊!!!




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