Java 多线程卖票

在实际应用中,我们经常用到多线程,如车站的售票系统,车站的各个售票口相当于各个线程。当我们做这个系统的时候可能会想到两种方式来实现,继承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();
    }

}

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

实现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();
    }

}

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

线程安全

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

synchronized

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

Java 多线程卖票_第5张图片

多次跑会出现这种情况,还是线程不安全,思考一下,如果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--));
                }
            }
        }
    }
}

Java 多线程卖票_第6张图片
线程安全,数据正常。
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--));
        }
    }
}

Java 多线程卖票_第7张图片
线程安全,数据正常。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

你可能感兴趣的:(java)