----------------------
ASP.Net+Unity开发、
.Net培训、期待与您交流! ----------------------
火车票售票模拟
在多线程那一篇的最后留了一道大题,本篇是为解出那道题,并通过题目深入学习线程的更多知识。
首先看一下我写的最终代码:
class Ticket implements Runnable //实现runnable接口
{
private int tickets = 100 ;
Object obj = new Object() ; //准备一个锁
@Override
public void run() //覆写run方法
{
while(true)
{
synchronized (obj) { //加入同步锁
if (tickets>0){
System.out.println(Thread.currentThread().getName()+":was saled....."+tickets--);
}
}
}
}
}
public class Demo {
public static void main(String[] args) {
Ticket td = new Ticket() ;
Thread t1 = new Thread(td, "一号窗口") ; //传递需要多线程实现的对象和线程命名
Thread t2 = new Thread(td, "二号窗口") ;
Thread t3 = new Thread(td, "三号窗口") ;
Thread t4 = new Thread(td, "四号窗口") ;
t1.start(); //启动线程
t2.start();
t3.start();
t4.start();
}
}
输出结果比较多,有一百行,博客里不方便展示,感兴趣的朋友运行一下上面的代码就一目了然。
之前的介绍中,只讲到了一个Thread类,如果你有去查API就会发现,Thread类实现了一个Runnable 接口,而这个接口就只有一个run方法,由此可以知道这个接口也可以告诉线程你要去运行我的run方法。Java里的继承有个明显的缺点,只能继承一个,当你的类已经继承了一个类的时候要实现多线程运行只能依赖接口的介入,Java的开发者们就提供了一个Runnable接口。开辟一个线程还是需要借助Thread类,但这时已经与继承Thread类有很大不同,因为调用了同一个对象所以数据共享,这个特点正好符合了售票的规则!
那么我用继承Thread类的方式能不能做到呢?也是可以的:
class Ticket extends Thread //继承Thread类
{
private static int tickets = 100 ; //静态化
Object obj = new Object() ;
@Override
public void run()
{
while(true)
{
synchronized (obj) {
if (tickets>0){
System.out.println(Thread.currentThread().getName()+":was saled....."+tickets--);
}
}
}
}
}
在主方法里你需要重新new出四个Ticket对象来,然后执行start方法。
把变数静态化即可,但是静态变量的生命周期过长,不利于资源有效利用,不推荐这种方式。
除了Runnable的加入,代码里还多了一个Object实例化对象和一个没见过的代码块。这个代码块是为了保证线性安全的一个锁,说这个之前还是应该体会一下线性安全的问题。
看一下这段代码:
class Ticket implements Runnable
{
private int tickets = 100 ;
@Override
public void run()
{
while(true)
{
if (tickets>0){
try{Thread.sleep(10);}catch(Exception e){e.printStackTrace();} //为了模拟现实中CPU处理多个任务的状态加个睡眠
System.out.println(Thread.currentThread().getName()+":was saled....."+tickets--);
}
}
}
}
乍一看觉得没问题啊,根本没什么错误。语句里确实没有错误,但是在运行的时候,难免计算机会有CPU繁忙的时候,或者电脑老化导致反应慢,以及其他问题导致的任务处理速度下降。当线程1刚刚判断完if语句,线程2抢着进来,线程2也判断完if语句又被其他程序抢占资源。这时线程3卖出了最后一个票,tickets相应减一变为0,轮到线程1卖票就卖出了一个0号票,轮到线程2更是卖出了不应该的-1号票。
输出结果
四号窗口:was saled.....4
一号窗口:was saled.....3
二号窗口:was saled.....2
三号窗口:was saled.....1
四号窗口:was saled.....0
一号窗口:was saled.....-1
二号窗口:was saled.....-1
如上所示。
线程安全问题导致售出异常票,如果真是全国火车票系统出了这个问题造成的经济损失将不可估量了。
线程锁的作用就是,当一个线程在处理共享数据的时候会把别的线程都挡在门外,用什么挡呢,一个标记对象,传递的Object当作标记,有线程时阻挡别的线程,没有线程时放入一个线程。比较形象的比喻就是火车上的卫生间,一个车厢一百多人当作一百多个线程,有一个人进入卫生间门锁就会显示有人,别的人(线程)就无法进入。这样就保证了共享数据的同步性!
只需要把计算共享数据的地方放进代码块,其他的不用管。如果只有一个线程操作该对象也不需要同步代码块。
到此为止,火车票的模拟也就完美完成
到此为止,火车票的模拟也就完美完成
对线程的理解是不是更深了一些呢?
此题重点在于指明线程是存在安全问题的,线程安全是使用线程时必须注意的环节,因为有的时候不去管线程安全有可能你自己测试上百次也没有问题,一运行于真实环境立刻显现不应有的错误。
----------------------
ASP.Net+Unity开发、
.Net培训、期待与您交流! ----------------------