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

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

5黑马币
这是我的一个电影院卖票的代码, 我加了锁对象,为什么运行 还是还出现0票和-1票的情况,请大神给解答一下
package sellTicket;

public class SellTicket implements Runnable {
        int ticket = 100;
        public final static Object obj =new Object();

        public SellTicket() {

        }

        @Override
        public void run() {
                while (ticket > 0) {
                        synchronized (obj) {
                                try {
                                        Thread.sleep(100);
                                } catch (InterruptedException e) {
                                        e.printStackTrace();
                                }
                                System.out.println(Thread.currentThread().getName()
                                                + "正在销售复仇者联盟的第" + (ticket--) + "张票...");
                        }
                }
        }
}



package sellTicket;

public class SellTicketDemo {
        public static void main(String[] args){
                SellTicket st=new SellTicket();
                Thread th1=new Thread(st,"窗口1");
                Thread th2=new Thread(st,"窗口2");
                Thread th3=new Thread(st,"窗口3");
                th1.start();
                th2.start();
                th3.start();
        }
}



运行结果:
....
窗口1正在销售复仇者联盟的第12张票...
窗口1正在销售复仇者联盟的第11张票...
窗口1正在销售复仇者联盟的第10张票...
窗口1正在销售复仇者联盟的第9张票...
窗口1正在销售复仇者联盟的第8张票...
窗口1正在销售复仇者联盟的第7张票...
窗口1正在销售复仇者联盟的第6张票...
窗口1正在销售复仇者联盟的第5张票...
窗口1正在销售复仇者联盟的第4张票...
窗口1正在销售复仇者联盟的第3张票...
窗口1正在销售复仇者联盟的第2张票...
窗口1正在销售复仇者联盟的第1张票...
窗口3正在销售复仇者联盟的第0张票...
窗口2正在销售复仇者联盟的第-1张票...



我把线程沉睡的这一块代码去掉,或者加在while循环前面都没有用,还是会出现0和-1,只是程序的运行速度变快了,请大神给解答下这道题,为什么给了锁对象之后,还是会出现多个线程操作同一个数据的问题..难道不是应该等待一个线程运行完毕,解锁后 下一个线程抢到cup控制权 然后判断条件,进来执行语句吗?

最佳答案

查看完整内容

:lol 简单说你只加了锁,没加判断 加锁的目的是保证一次只允许一个进程访问资源,一般都是两个判断的结构 一个是锁:判断的是 资源是不是正被线程访问, 另一个是资源:判断资源是否满足要求 也就是说,你需要在锁的内部加一个判断语句,来确定是不是调用资源 你的代码明显有锁,但只是判断了线程是否单一(休眠),没有判断资源(余票) 这种问题,只要记住两点,第一:保证操作资源的线程只有一个,也就是加锁,第二锁内部,是 ...

19 个回复

倒序浏览
:lol 简单说你只加了锁,没加判断
加锁的目的是保证一次只允许一个进程访问资源,一般都是两个判断的结构
一个是锁:判断的是 资源是不是正被线程访问,
另一个是资源:判断资源是否满足要求
也就是说,你需要在锁的内部加一个判断语句,来确定是不是调用资源

你的代码明显有锁,但只是判断了线程是否单一(休眠),没有判断资源(余票)
这种问题,只要记住两点,第一:保证操作资源的线程只有一个,也就是加锁,第二锁内部,是否判断资源存在
就像,你进仓库,第一步开锁,第二步,看看有没有东西,最后才是操作数据,拿东西
回复 使用道具 举报
这个是判断条件放错位置了,这个我以前遇到过。记得是因为3个线程在票数为1的时候进入了判断,结果都通过了判断,于是一个线程进去了,另外两个在等待资源,于是就会造成输出0和-1。最后不了了之用了同步方法直接解决。
回复 使用道具 举报
  1. class Ticket implements Runnable
  2. {
  3.         private  int tick = 1000;
  4.         Object obj = new Object();
  5.         public void run()
  6.         {
  7.                 while(true)
  8.                 {
  9.                         synchronized(obj)
  10.                         {
  11.                                 if(tick>0)
  12.                                 {
  13.                                         try{Thread.sleep(10);}catch(Exception e){}
  14.                                         System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--);
  15.                                 }
  16.                         }
  17.                 }
  18.         }
  19. }

  20. class  TicketDemo2
  21. {
  22.         public static void main(String[] args)
  23.         {
  24.                 Ticket t = new Ticket();

  25.                 Thread t1 = new Thread(t);
  26.                 Thread t2 = new Thread(t);
  27.                 Thread t3 = new Thread(t);
  28.                 Thread t4 = new Thread(t);
  29.                 t1.start();
  30.                 t2.start();
  31.                 t3.start();
  32.                 t4.start();
  33.         }
  34. }
复制代码
回复 使用道具 举报
synchronized (obj) {}代码块里添加一个判断if(ticket>0){}
回复 使用道具 举报
假设在ticket为1时,有2个线程进入个线程 while (ticket > 0) 循环,其中1个线程在synchronized (obj) {}代码块前等待,1个进入里面执行ticket--,并输出0,这时第3个线程不满足(ticket > 0) 进不了while循环,在synchronized (obj) {}代码块前等待的线程拿到锁后进入进行ticket--,并输出-1.所以在ticket--逻辑要加判断if(ticket>0),成立就进行ticket--,否则就不进行ticket--。
回复 使用道具 举报
一定要注意加锁的位置,要考虑如果有数个线程都停留在了加锁的位置,一旦解锁,会出现什么样的状况。
回复 使用道具 举报
控制语句都应该放在锁里面啊
回复 使用道具 举报
package cn.itcast.thread;
/*
* 100张售票
* 线程采用实现接口方式,不采用继承Thread类
*
* 实现接口方式,和继承Thread方式
*   实现接口方式,避免单继承局限性
*   实现接口方式,让线程中的数据共享
*   
* 以后全部采用接口方式
*
* 使用同步技术,保证线程的安全
* 同步技术:保证共享数据,只有1个线程操作
*
* 关键字 synchronized
*   synchronized(任意对象){
*      线程操作的共享数据
*   }
*  同步代码块,保证线程数据安全
*/

//定义类,实现Runnable接口,重写run方法
class TicketRunnable implements Runnable{
        private int tickets = 100;
        private Object obj = new Object();
        public void run(){
                while(true){
                 synchronized(obj){       
                        if(tickets>0){
                                try{Thread.sleep(10);}catch(Exception e){}
                                System.out.println(Thread.currentThread().getName()+"出售第"+tickets--);
                        }
                 }
                }
        }
}

public class ThreadDemo8 {
        public static void main(String[] args) {
                //创建Thread类对象,传递Runnable接口的实现类的对象
                TicketRunnable t = new TicketRunnable();
                Thread t0 = new Thread(t);
                Thread t1 = new Thread(t);
                Thread t2 = new Thread(t);
               
                t0.start();
                t1.start();
                t2.start();
        }
}
回复 使用道具 举报
adkai 初级黑马 2015-5-27 11:53:43
10#
存在问题:我想你是判断的位置出错了,你的线程是在循环里面锁的,当3个线程判断到票数在1的时候,3个线程都进入了循环里面,就是有了执行的权限,然后当第一个线程执行完,此时票数为0,但是后面还有两个线程是在第一线程执行后有权限执行的,那么票数就是0和-1了。
问题证据:不信的话,你可以实例n个线程试一下,结果就是会多出n-1个执行的结果,就是出现-(n-2)的票数。假如实例8个窗口,会出现最多-6的票数
解决:在判断循环之外进行加锁线程,或者在源程序的情况下,在每一次票数--之后立刻判断此时的票数,当票数为0时使用break跳出循环。
最后,希望可以帮助到你,不知道我的表达够不够清晰
回复 使用道具 举报
在锁里面要判断  tick>0   
回复 使用道具 举报
锁对象搞个简单的就行,输出语句判断存在问题
回复 使用道具 举报
System.out.println(Thread.currentThread().getName()
                                                 + "正在销售复仇者联盟的第" + (ticket--) + "张票...");
必须要在这个语句上免加判断语句if(ticket>0)要不然线程进来while循环,判断为真了,这个时候睡眠,然一个线程也进来while循环,这个时候ticket还是1也会进入循环,这样下次两个线程都启动的时候就不用在判断ticket是不是大于0了。这样就会出现0  -1的票了
回复 使用道具 举报
当票数为1时,窗口1线程判断票数大于0,窗口1线程进入循环同时加上锁,然后休眠,而窗口2线程,窗口3线程先后判断,票数任然为1,进入循环等待,等窗口1线程唤醒后解锁,而窗口2线程,窗口3线程线程先后执行,产生0张,-1张票,所以循环也要加在锁里面
代码改成:
package sellTicket;

public class SellTicket implements Runnable {
        int ticket = 100;
        public final static Object obj =new Object();

        public SellTicket() {

        }

        @Override
        public void run() {
                synchronized (obj) {
                while (ticket > 0) {
                                try {
                                        Thread.sleep(100);
                                } catch (InterruptedException e) {
                                        e.printStackTrace();
                                }
                                System.out.println(Thread.currentThread().getName()
                                                + "正在销售复仇者联盟的第" + (ticket--) + "张票...");
                        }
                }
        }
}



package sellTicket;

public class SellTicketDemo {
        public static void main(String[] args){
                SellTicket st=new SellTicket();
                Thread th1=new Thread(st,"窗口1");
                Thread th2=new Thread(st,"窗口2");
                Thread th3=new Thread(st,"窗口3");
                th1.start();
                th2.start();
                th3.start();
        }
}
回复 使用道具 举报
刚开始学Java  不太懂 哦

点评

你是来蹭经验的嘛?怎么每个帖子你都是这句话?  发表于 2015-5-28 09:14
回复 使用道具 举报
呵呵 顶一个
回复 使用道具 举报
锁的位置啊
回复 使用道具 举报
认真学习下
回复 使用道具 举报
锁里面也要判断票数是否大于0
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马