Java 多线程卖票

来源:互联网 发布:淘宝宝贝图片宽度 编辑:程序博客网 时间:2024/05/17 06:13

在实际应用中,我们经常用到多线程,如车站的售票系统,车站的各个售票口相当于各个线程。当我们做这个系统的时候可能会想到两种方式来实现,继承Thread类或实现Runnable接口,现在看一下这两种方式实现的两种结果。

继承Thread类:

class MyThread extends Thread {    private int ticket = 5;    private String name;    public MyThread(String name) {        this.name = name;    }    public void run() {     //for监控每个窗口卖票的张数,必须有,不然只买1张就结束        for (int i = 0; i < 500; i++) {            if (this.ticket > 0) {                System.out.println(this.name + "第"+i+"次卖票---->" + (this.ticket--));            }        }    }}public class TicketsThread {    public static void main(String[] args) {        //创建三个线程,模拟三个窗口卖票        MyThread mt1 = new MyThread("一号窗口");        MyThread mt2 = new MyThread("二号窗口");        MyThread mt3 = new MyThread("三号窗口");        //启动这三个线程,即窗口,开始卖票        mt1.start();        mt2.start();        mt3.start();    }}

运行结果:
这里写图片描述
继承Thread类的,我们相当于拿出三件事即三个卖票5张的任务分别分给三个窗口,他们各做各的事各卖各的票各完成各的任务,因为MyThread继承Thread类,所以在new MyThread的时候在创建三个对象的同时创建了三个线程;
这里写图片描述

实现Runable方法

class MyThread1 implements Runnable{    private int ticket =10;    private String name;    public void run(){        for(int i =0;i<500;i++){            if(this.ticket>0){                   System.out.println(Thread.currentThread().getName()+"第"+i+"次"+"卖票---->"+(this.ticket--));            }        }    }}public class TicketsRunable {    public static void main(String[] args) {        // TODO Auto-generated method stub        //设计三个线程        MyThread1 mt = new MyThread1();        Thread t1 = new Thread(mt,"一号窗口");        Thread t2 = new Thread(mt,"二号窗口");        Thread t3 = new Thread(mt,"三号窗口");//         MyThread1 mt2 = new MyThread1();//         MyThread1 mt3 = new MyThread1();        t1.start();        t2.start();        t3.start();    }}

运行结果
这里写图片描述
实现Runnable的, 相当于是拿出一个卖票10张得任务给三个人去共同完成,new MyThread1相当于创建一个任务,然后实例化三个Thread,创建三个线程即安排三个窗口去执行。
这里写图片描述

线程安全

上面的两种方式是实现多线程的基本方式,但是在运行的时候有的时候发现同一张票会卖2次,线程不安全,数据未同步。

synchronized

  public void run() {        while (this.ticket > 0) {            synchronized (this) {                System.out.println(Thread.currentThread().getName() + "第" + "次" + "卖票---->" + (this.ticket--));            }        }    }

这里写图片描述

多次跑会出现这种情况,还是线程不安全,思考一下,如果2 3窗口在还有1张被锁住,等待完资源,此时数据已经脏了。
this.ticket这部操作,将ticket从主内存读取到工作内存,2号 3号同时读取了2个副本,因此可能出现不安全情况。

class MyThread1 implements Runnable {    private int ticket = 20;    private String name;    public void run() {        while (true) {            synchronized (this) {                if (this.ticket > 0) {                    System.out.println(Thread.currentThread().getName() + "第" + "次" + "卖票---->" + (this.ticket--));                }            }        }    }}

这里写图片描述
线程安全,数据正常。
1. 获得同步锁;
2. 清空工作内存;
3. 从主内存拷贝对象副本到工作内存;this.ticket到工作内存
4. 执行代码(计算或者输出等);
5. 刷新主内存数据;
6. 释放同步锁。
所以使用synchronized ,锁的位置很重要!!!

volatile

class MyThread1 implements Runnable{    private volatile int ticket =20;    private String name;    public  void run(){       while (this.ticket>0){                                     System.out.println(Thread.currentThread().getName()+"第"+"次"+"卖票---->"+(this.ticket--));        }    }}

这里写图片描述
线程安全,数据正常。volatile
1. 将变量i从主内存拷贝到工作内存;
2. 改变i的值;
3. 刷新主内存数据;
4. 将变量j从主内存拷贝到工作内存;
5. 改变j的值;
6. 刷新主内存数据;

参考:
http://mars914.iteye.com/blog/1508429
http://blog.csdn.net/mccand1234/article/details/52130271

0 0