当前位置:编程学习 > JAVA >>

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

小弟在编写线程时遇到一个令我费解的问题,同样的思路,但是结果不一样
程序一:

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楼的方式加锁是可以的。只不过这个锁加的范围有点大了,这样就和单线程没有区别了。可以对程序做一些调整,适当减小加锁的范围。 --------------------编程问答--------------------
引用 楼主 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;
            }
        }
    }
}
--------------------编程问答--------------------
引用 3 楼 tiwerbao 的回复:

我再补充一下,由于“if (ticketControl-- > 0) {”不是原子操作,其实还是有冲突的隐患,所以再加个锁更加稳妥,而且也不怎么影响性能,改成如下:

int nTicket = 0;
         synchronized(this) {
         nTicket = ticketControl--;
         }
            if (nTicket > 0) {
--------------------编程问答-------------------- 的二个是人品好  多运行几次总会出负的  第一个因为有sleep所以隐患比较明显罢了 --------------------编程问答-------------------- 第一个

引用 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();
 
    }
 
}



稍做了一下修改 这个是同步问题 错票 负票都是没上锁的原因 



加成那样的锁之后,所有的票都被一个线程抢到了,而且永远的第二个线程,就算我改了线程的优先级也不行 --------------------编程问答--------------------
引用 5 楼 qq14344545 的回复:
的二个是人品好  多运行几次总会出负的  第一个因为有sleep所以隐患比较明显罢了


当我把第一个的sleep去掉后还是有负票,当我把第二个票数改到2000的时候,还是没有负票。 --------------------编程问答--------------------
引用 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;
            }
        }
    }
}


恩,还没试,但是逻辑上应该正确,,但是你在if里加了个--,下面的--ticket就可以去掉了。
你还是太粗心,O(∩_∩)O哈哈~ --------------------编程问答--------------------
引用 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;
            }
        }
    }
}


恩,还没试,但是逻辑上应该正确,,但是你在if里加了个--,下面的--ticket就可以去掉了。
你还是太粗心,O(∩_∩)O哈哈~

我用了两个参数呢,两个是不同的!你跑一下就知道了,如果只用一个的话,会冲突,出现负数的票数。用两个就不会了!所以--ticket是要的
补充:Java ,  Java SE
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,