线程问题--火车站卖票问题
小弟在编写线程时遇到一个令我费解的问题,同样的思路,但是结果不一样程序一:
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张票
虽然没有负票,但是只有第二个线程在执行,而且试了几次也一直是第二个线程,求解 线程 javase 单例 --------------------编程问答--------------------
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();
}
}
稍做了一下修改 这个是同步问题 错票 负票都是没上锁的原因 --------------------编程问答-------------------- 其实你两个程序都一样有问题,至于你第二个问题没有体现出来是因为时间太短了。
第一个里面有sleep,第二个却没有,如果第二个把sleep加上的话,效果会是一样的。
按照1楼的方式加锁是可以的。只不过这个锁加的范围有点大了,这样就和单线程没有区别了。可以对程序做一些调整,适当减小加锁的范围。 --------------------编程问答--------------------
楼主一开始的两个程序,都是多线程去抢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;
}
}
}
}
我再补充一下,由于“if (ticketControl-- > 0) {”不是原子操作,其实还是有冲突的隐患,所以再加个锁更加稳妥,而且也不怎么影响性能,改成如下:
--------------------编程问答-------------------- 的二个是人品好 多运行几次总会出负的 第一个因为有sleep所以隐患比较明显罢了 --------------------编程问答-------------------- 第一个
int nTicket = 0;
synchronized(this) {
nTicket = ticketControl--;
}
if (nTicket > 0) {
加成那样的锁之后,所有的票都被一个线程抢到了,而且永远的第二个线程,就算我改了线程的优先级也不行 --------------------编程问答--------------------
当我把第一个的sleep去掉后还是有负票,当我把第二个票数改到2000的时候,还是没有负票。 --------------------编程问答--------------------
恩,还没试,但是逻辑上应该正确,,但是你在if里加了个--,下面的--ticket就可以去掉了。
你还是太粗心,O(∩_∩)O哈哈~ --------------------编程问答--------------------
楼主一开始的两个程序,都是多线程去抢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;
}
}
}
}
恩,还没试,但是逻辑上应该正确,,但是你在if里加了个--,下面的--ticket就可以去掉了。
你还是太粗心,O(∩_∩)O哈哈~
我用了两个参数呢,两个是不同的!你跑一下就知道了,如果只用一个的话,会冲突,出现负数的票数。用两个就不会了!所以--ticket是要的
补充:Java , Java SE