欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2024-01-23 15:07:10
首先看题目描述: 假设有火车票100张,创建4个线程模拟4个售票点,每100ms售出一张,打印出售票过程,格式如下: 窗口3:卖出第100张票 窗口4:卖出第99张票 ............ ............ 简单的思路就是创建一个类,首先肯定要去继承Thread。开启线程,由于是4个窗口 ......

首先看题目描述:

假设有火车票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--+"张票。");
            }
        }
    }    
}