JAVA学习之路(多线程)---模拟售票(细解)

首先看题目描述:

假设有火车票100张,创建4个线程模拟4个售票点,每100ms售出一张,打印出售票过程,格式如下:

窗口3:卖出第100张票

窗口4:卖出第99张票 

............

............

简单的思路就是创建一个类,首先肯定要去继承Thread。开启线程,由于是4个窗口,肯定要开启4个线程。然后让每个线程去输出结果,也就是卖出去的票。这里很多人想不到如何让4个线程不打印重复的票。(比如4个线程都卖出去了第100张票,这显然是不合理的)。

看代码:

  

 1 package com.lesson.thread;
 2 
 3 public class MyThread {
 4 
 5     public static void main(String[] args) {
 6         Ticket sell1 = new Ticket();
 7         Ticket sell2 = new Ticket();
 8         Ticket sell3 = new Ticket();
 9         Ticket sell4 = new Ticket();
10         sell1.setName("窗口1");
11         sell2.setName("窗口2");
12         sell3.setName("窗口3");
13         sell4.setName("窗口4");
14         sell1.start();
15         sell2.start();
16         sell3.start();
17         sell4.start();
18     }
19 }
20 class Ticket extends Thread {
21     private static int tickets = 100;//这里设置成static,目的是让每个线程共享这个变量。以免出现重复打印的现象。
22     @Override
23     public void run() {
24         while(true) {
25             if(tickets <= 0) {
26                 break;
27             }
2829          System.out.println(getName()+":买出第"+tickets--+"张票。");//卖出一张减一张票
30 31         }
32     }
33     
34 }

可能你已经看到了你想要的结果了。但是,还没完。目前代码写到这里是有问题的!!!

为什么?

看下面的代码:

package thread;
public class Mythread {

    public static void main(String[] args) {
        Ticket sell1 = new Ticket();
        Ticket sell2 = new Ticket();
        Ticket sell3 = new Ticket();
        Ticket sell4 = new Ticket();
        sell1.setName("窗口1");
        sell2.setName("窗口2");
        sell3.setName("窗口3");
        sell4.setName("窗口4");
        sell1.start();
        sell2.start();
        sell3.start();
        sell4.start();
    }
}
class Ticket extends Thread {
    private static int tickets = 100;
    @Override
    public void run() {
        while(true) {
            if(tickets <= 0) {
                break;
            }
            try {
                Thread.sleep(10);          //让进来的线程睡10ms;线程1,2,3,4都睡在这
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(getName()+":买出了第"+tickets--+"张票。");
        }
    }    
}

与开始的代码不同在于让进来的线程睡一会。可以看到如下运行结果:

窗口2:买出了第0张票。
窗口3:买出了第-1张票。
窗口1:买出了第-2张票

这里编者调出来了出问题的地方。可以看到怎么会第0,-1,-2张票???

做出解释:

其实在这里让线程睡一会就是为了演示这里有很多行代码要执行。假设票已经卖到第1张了,也就是tickets=1,然后第一条线程进来判断 tickets <= 0 ?不成立,然后线程1要睡 10ms,紧接着,线程2进来,这时 tickets 还是为1,因为线程1在睡,tickets 没有减 。然后线程2判断 tickets <= 0 ? 还是不成立,线程2又开始睡。同样,线程3,线程4都睡了。 这时的tickets 还是等于1的。然后线程1先醒过来,开始输出结果,tickets 减了1。可是这是其他的线程还是经过while里面的判断语句进来了的,只是睡了。所以当其他线程醒过来的时候,还是会打印出结果的。也就出现了上面的问题。

解决方法:

 多线程并发改变同一变量,为了解决,采用同步代码块synchronized。里面加任意的对象,但是不能加this,因为这里创建了四个线程,每一个线程都有自己的对象,所以是四个不同的对象,没有用。所以这里不能用this,必须锁在同一个对象里才行。而Thickets.class这是唯一的。

package thread;
public class Mythread {

    public static void main(String[] args) {
        Ticket sell1 = new Ticket();
        Ticket sell2 = new Ticket();
        Ticket sell3 = new Ticket();
        Ticket sell4 = new Ticket();
        sell1.setName("窗口1");
        sell2.setName("窗口2");
        sell3.setName("窗口3");
        sell4.setName("窗口4");
        sell1.start();
        sell2.start();
        sell3.start();
        sell4.start();
    }
}
class Ticket extends Thread {
    private static int tickets = 100;
    @Override
    public void run() {
        while(true) {
            synchronized (Ticket.class) {//同步代码块
                if(tickets <= 0) {
                    break;
                }
                try {
                    Thread.sleep(100);          //每100ms卖出一张
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(getName()+":买出了第"+tickets--+"张票。");
            }
        }
    }    
}

 

posted @ 2018-12-27 15:19  bestwell  阅读(1743)  评论(0编辑  收藏  举报