线程问题--火车站卖票问题

石大菜鸟 2013-11-12 05:56:41
小弟在编写线程时遇到一个令我费解的问题,同样的思路,但是结果不一样
程序一:

public class Ticket extends Thread {

private int ticket = 5;

@Override
public void run() {
// synchronized (this) {

while (true) {
if (ticket > 0) {
System.out.println("顾客到达 "
+ Thread.currentThread().getName() + "售票台");
System.out.println("售票台" + Thread.currentThread().getName()
+ "准备出售第" + ticket + "张票");
try {// 具体卖票过程
System.out.println("售票台需要5分钟出售这张票");
for (int i = 1; i < 5; i++) {
Thread.sleep(1000);
System.out.println(i + "分钟");
}
System.out.println("售票台"
+ Thread.currentThread().getName() + "卖完第"
+ ticket + "张票");
System.out.println("顾客从"
+ Thread.currentThread().getName() + "买到第"
+ ticket + "张票");
--ticket;
} catch (InterruptedException e) {
System.out.println("卖票不成功");
System.exit(1);
}
} else {
break;
}
}
// }
}

}
public static void main(String[] args) {
Ticket t = new Ticket();
Thread s = new Thread(t);
Thread s1 = new Thread(t);
Thread s2 = new Thread(t);
Thread s3 = new Thread(t);
s.start();
s1.start();
s2.start();
s3.start();

}

执行结果:
顾客到达 Thread-1售票台
顾客到达 Thread-3售票台
顾客到达 Thread-2售票台
顾客到达 Thread-4售票台
售票台Thread-2准备出售第5张票
售票台Thread-3准备出售第5张票
售票台需要5分钟出售这张票
售票台Thread-1准备出售第5张票
售票台需要5分钟出售这张票
售票台需要5分钟出售这张票
售票台Thread-4准备出售第5张票
售票台需要5分钟出售这张票
1分钟
1分钟
1分钟
1分钟
2分钟
2分钟
2分钟
2分钟
3分钟
3分钟
3分钟
3分钟
4分钟
4分钟
售票台Thread-1卖完第5张票
售票台Thread-3卖完第5张票
顾客从Thread-1买到第5张票
顾客从Thread-3买到第5张票
顾客到达 Thread-1售票台
顾客到达 Thread-3售票台
售票台Thread-1准备出售第3张票
售票台需要5分钟出售这张票
售票台Thread-3准备出售第3张票
售票台需要5分钟出售这张票
4分钟
售票台Thread-4卖完第3张票
4分钟
顾客从Thread-4买到第3张票
售票台Thread-2卖完第3张票
顾客到达 Thread-4售票台
售票台Thread-4准备出售第2张票
售票台需要5分钟出售这张票
顾客从Thread-2买到第2张票
顾客到达 Thread-2售票台
售票台Thread-2准备出售第1张票
售票台需要5分钟出售这张票
1分钟
1分钟
1分钟
1分钟
2分钟
2分钟
2分钟
2分钟
3分钟
3分钟
3分钟
3分钟
4分钟
售票台Thread-3卖完第1张票
顾客从Thread-3买到第1张票
4分钟
售票台Thread-1卖完第0张票
顾客从Thread-1买到第0张票
4分钟
售票台Thread-4卖完第-1张票
顾客从Thread-4买到第-1张票
4分钟
售票台Thread-2卖完第-2张票
顾客从Thread-2买到第-2张票


程序二:


public class Test4 {

public static void main(String[] args) {
TestThread4 t = new TestThread4();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();

}

}

class TestThread4 extends Thread {
private int tick =20;

public void run() {
while (true) {
if (tick > 0) {
System.out.println(Thread.currentThread().getName() + "出售票"
+ tick--);
} else {
break;
}
}
}
}




运行结果:
Thread-1出售票20
Thread-1出售票17
Thread-3出售票18
Thread-2出售票19
Thread-3出售票14
Thread-3出售票12
Thread-1出售票15
Thread-4出售票16
Thread-1出售票10
Thread-3出售票11
Thread-2出售票13
Thread-3出售票7
Thread-1出售票8
Thread-4出售票9
Thread-1出售票4
Thread-3出售票5
Thread-2出售票6
Thread-3出售票1
Thread-1出售票2
Thread-4出售票3


疑惑:为什么第一个程序会卖到负票,第二个却不会。求大神讲解
如果第一个程序把单例的注释去掉,执行结果如下:
顾客到达 Thread-1售票台
售票台Thread-1准备出售第5张票
售票台需要5分钟出售这张票
1分钟
2分钟
3分钟
4分钟
售票台Thread-1卖完第5张票
顾客从Thread-1买到第5张票
顾客到达 Thread-1售票台
售票台Thread-1准备出售第4张票
售票台需要5分钟出售这张票
1分钟
2分钟
3分钟
4分钟
售票台Thread-1卖完第4张票
顾客从Thread-1买到第4张票
顾客到达 Thread-1售票台
售票台Thread-1准备出售第3张票
售票台需要5分钟出售这张票
1分钟
2分钟
3分钟
4分钟
售票台Thread-1卖完第3张票
顾客从Thread-1买到第3张票
顾客到达 Thread-1售票台
售票台Thread-1准备出售第2张票
售票台需要5分钟出售这张票
1分钟
2分钟
3分钟
4分钟
售票台Thread-1卖完第2张票
顾客从Thread-1买到第2张票
顾客到达 Thread-1售票台
售票台Thread-1准备出售第1张票
售票台需要5分钟出售这张票
1分钟
2分钟
3分钟
4分钟
售票台Thread-1卖完第1张票
顾客从Thread-1买到第1张票



虽然没有负票,但是只有第二个线程在执行,而且试了几次也一直是第二个线程,求解
...全文
386 9 打赏 收藏 转发到动态 举报
写回复
用AI写文章
9 条回复
切换为时间正序
请发表友善的回复…
发表回复
Jacky-止涯 2013-11-25
  • 打赏
  • 举报
回复
引用 8 楼 u010675319 的回复:
[quote=引用 3 楼 tiwerbao 的回复:] [quote=引用 楼主 u010675319 的回复:]
楼主一开始的两个程序,都是多线程去抢ticket,不上锁肯定会冲突的,楼上说得对,第二个没有出现负数是因为时间太短而已! 试想这么一个场景: 到最后一张票的时候,4个线程同时检查“ticket > 0”进去了,进去了就会卖出来,可是只有一张票,所以出现负数。 所以此处重点在于同步好这些ticket。而且考虑到性能问题,把锁的范围缩得越小越好。楼主这里只要加个控制变量 ticketControl 就可以很好解决线程冲突的问题,而且能使效率最大化。

class Ticket extends Thread {
	 
    private volatile int ticket = 5;
    private volatile int ticketControl = ticket;
 
    @Override
    public void run() {
        while (true) {
            if (ticketControl-- > 0) {
                System.out.println("顾客到达 "
                        + Thread.currentThread().getName() + "售票台");
                System.out.println("售票台" + Thread.currentThread().getName()
                        + "准备出售第" + ticket + "张票");
                try {// 具体卖票过程
                    System.out.println("售票台需要5分钟出售这张票");
                    for (int i = 1; i < 5; i++) {
                        Thread.sleep(100);
                        System.out.println(i + "分钟");
                    }
                    System.out.println("售票台"
                            + Thread.currentThread().getName() + "卖完第"
                            + ticket + "张票");
                    System.out.println("顾客从"
                            + Thread.currentThread().getName() + "买到第"
                            + ticket + "张票");
                    --ticket;
                } catch (InterruptedException e) {
                    System.out.println("卖票不成功");
                    System.exit(1);
                }
            } else {
                break;
            }
        }
    }
}
[/quote] 恩,还没试,但是逻辑上应该正确,,但是你在if里加了个--,下面的--ticket就可以去掉了。 你还是太粗心,O(∩_∩)O哈哈~[/quote] 我用了两个参数呢,两个是不同的!你跑一下就知道了,如果只用一个的话,会冲突,出现负数的票数。用两个就不会了!所以--ticket是要的
石大菜鸟 2013-11-14
  • 打赏
  • 举报
回复
引用 3 楼 tiwerbao 的回复:
[quote=引用 楼主 u010675319 的回复:]
楼主一开始的两个程序,都是多线程去抢ticket,不上锁肯定会冲突的,楼上说得对,第二个没有出现负数是因为时间太短而已! 试想这么一个场景: 到最后一张票的时候,4个线程同时检查“ticket > 0”进去了,进去了就会卖出来,可是只有一张票,所以出现负数。 所以此处重点在于同步好这些ticket。而且考虑到性能问题,把锁的范围缩得越小越好。楼主这里只要加个控制变量 ticketControl 就可以很好解决线程冲突的问题,而且能使效率最大化。

class Ticket extends Thread {
	 
    private volatile int ticket = 5;
    private volatile int ticketControl = ticket;
 
    @Override
    public void run() {
        while (true) {
            if (ticketControl-- > 0) {
                System.out.println("顾客到达 "
                        + Thread.currentThread().getName() + "售票台");
                System.out.println("售票台" + Thread.currentThread().getName()
                        + "准备出售第" + ticket + "张票");
                try {// 具体卖票过程
                    System.out.println("售票台需要5分钟出售这张票");
                    for (int i = 1; i < 5; i++) {
                        Thread.sleep(100);
                        System.out.println(i + "分钟");
                    }
                    System.out.println("售票台"
                            + Thread.currentThread().getName() + "卖完第"
                            + ticket + "张票");
                    System.out.println("顾客从"
                            + Thread.currentThread().getName() + "买到第"
                            + ticket + "张票");
                    --ticket;
                } catch (InterruptedException e) {
                    System.out.println("卖票不成功");
                    System.exit(1);
                }
            } else {
                break;
            }
        }
    }
}
[/quote] 恩,还没试,但是逻辑上应该正确,,但是你在if里加了个--,下面的--ticket就可以去掉了。 你还是太粗心,O(∩_∩)O哈哈~
石大菜鸟 2013-11-14
  • 打赏
  • 举报
回复
引用 5 楼 qq14344545 的回复:
的二个是人品好 多运行几次总会出负的 第一个因为有sleep所以隐患比较明显罢了
当我把第一个的sleep去掉后还是有负票,当我把第二个票数改到2000的时候,还是没有负票。
石大菜鸟 2013-11-14
  • 打赏
  • 举报
回复
第一个
引用 1 楼 Candylibin 的回复:

public class Ticket implements Runnable {
	 
    private int ticket = 5;
 
    public void run() {
       
 
            while (true)
            	 synchronized (this) 
            	 {
	                if (ticket > 0) 
	                {
	                    System.out.println("顾客到达 "
	                            + Thread.currentThread().getName() + "售票台");
	                    System.out.println("售票台" + Thread.currentThread().getName()
	                            + "准备出售第" + ticket + "张票");
	                    try 
	                    {// 具体卖票过程
	                        /*System.out.println("售票台需要5分钟出售这张票");
	                        for (int i = 1; i < 5; i++) {
	                            Thread.sleep(1000);
	                            System.out.println(i + "分钟");
	                        }*/
	                        System.out.println("售票台"
	                                + Thread.currentThread().getName() + "卖完第"
	                                + ticket + "张票");
	                        System.out.println("顾客从"
	                                + Thread.currentThread().getName() + "买到第"
	                                + ticket + "张票");
	                        ticket--;
	                    } 
	                    catch (Exception e) 
	                    {
	                        System.out.println("卖票不成功");
	                        System.exit(1);
	                    }
	                } 
	               
                else 
                {
                    break;
                }
            
       }
    }
    
    public static void main(String[] args) {
        Ticket t = new Ticket();
         Thread s = new Thread(t);
         Thread s1 = new Thread(t);
         Thread s2 = new Thread(t);
         Thread s3 = new Thread(t);
         s.start();
         s1.start();
         s2.start();
         s3.start();
 
    }
 
}
稍做了一下修改 这个是同步问题 错票 负票都是没上锁的原因
加成那样的锁之后,所有的票都被一个线程抢到了,而且永远的第二个线程,就算我改了线程的优先级也不行
qq14344545 2013-11-13
  • 打赏
  • 举报
回复
的二个是人品好 多运行几次总会出负的 第一个因为有sleep所以隐患比较明显罢了
Jacky-止涯 2013-11-12
  • 打赏
  • 举报
回复
引用 3 楼 tiwerbao 的回复:
我再补充一下,由于“if (ticketControl-- > 0) {”不是原子操作,其实还是有冲突的隐患,所以再加个锁更加稳妥,而且也不怎么影响性能,改成如下:

int nTicket = 0;
        	synchronized(this) {
        		nTicket = ticketControl--;
        	}
            if (nTicket > 0) {
Jacky-止涯 2013-11-12
  • 打赏
  • 举报
回复
引用 楼主 u010675319 的回复:
楼主一开始的两个程序,都是多线程去抢ticket,不上锁肯定会冲突的,楼上说得对,第二个没有出现负数是因为时间太短而已! 试想这么一个场景: 到最后一张票的时候,4个线程同时检查“ticket > 0”进去了,进去了就会卖出来,可是只有一张票,所以出现负数。 所以此处重点在于同步好这些ticket。而且考虑到性能问题,把锁的范围缩得越小越好。楼主这里只要加个控制变量 ticketControl 就可以很好解决线程冲突的问题,而且能使效率最大化。

class Ticket extends Thread {
	 
    private volatile int ticket = 5;
    private volatile int ticketControl = ticket;
 
    @Override
    public void run() {
        while (true) {
            if (ticketControl-- > 0) {
                System.out.println("顾客到达 "
                        + Thread.currentThread().getName() + "售票台");
                System.out.println("售票台" + Thread.currentThread().getName()
                        + "准备出售第" + ticket + "张票");
                try {// 具体卖票过程
                    System.out.println("售票台需要5分钟出售这张票");
                    for (int i = 1; i < 5; i++) {
                        Thread.sleep(100);
                        System.out.println(i + "分钟");
                    }
                    System.out.println("售票台"
                            + Thread.currentThread().getName() + "卖完第"
                            + ticket + "张票");
                    System.out.println("顾客从"
                            + Thread.currentThread().getName() + "买到第"
                            + ticket + "张票");
                    --ticket;
                } catch (InterruptedException e) {
                    System.out.println("卖票不成功");
                    System.exit(1);
                }
            } else {
                break;
            }
        }
    }
}
rumlee 2013-11-12
  • 打赏
  • 举报
回复
其实你两个程序都一样有问题,至于你第二个问题没有体现出来是因为时间太短了。 第一个里面有sleep,第二个却没有,如果第二个把sleep加上的话,效果会是一样的。 按照1楼的方式加锁是可以的。只不过这个锁加的范围有点大了,这样就和单线程没有区别了。可以对程序做一些调整,适当减小加锁的范围。
Candylibin 2013-11-12
  • 打赏
  • 举报
回复

public class Ticket implements Runnable {
	 
    private int ticket = 5;
 
    public void run() {
       
 
            while (true)
            	 synchronized (this) 
            	 {
	                if (ticket > 0) 
	                {
	                    System.out.println("顾客到达 "
	                            + Thread.currentThread().getName() + "售票台");
	                    System.out.println("售票台" + Thread.currentThread().getName()
	                            + "准备出售第" + ticket + "张票");
	                    try 
	                    {// 具体卖票过程
	                        /*System.out.println("售票台需要5分钟出售这张票");
	                        for (int i = 1; i < 5; i++) {
	                            Thread.sleep(1000);
	                            System.out.println(i + "分钟");
	                        }*/
	                        System.out.println("售票台"
	                                + Thread.currentThread().getName() + "卖完第"
	                                + ticket + "张票");
	                        System.out.println("顾客从"
	                                + Thread.currentThread().getName() + "买到第"
	                                + ticket + "张票");
	                        ticket--;
	                    } 
	                    catch (Exception e) 
	                    {
	                        System.out.println("卖票不成功");
	                        System.exit(1);
	                    }
	                } 
	               
                else 
                {
                    break;
                }
            
       }
    }
    
    public static void main(String[] args) {
        Ticket t = new Ticket();
         Thread s = new Thread(t);
         Thread s1 = new Thread(t);
         Thread s2 = new Thread(t);
         Thread s3 = new Thread(t);
         s.start();
         s1.start();
         s2.start();
         s3.start();
 
    }
 
}
稍做了一下修改 这个是同步问题 错票 负票都是没上锁的原因

62,621

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧