您的位置:首页 > 编程语言 > Java开发

Java 多线程卖票

2016-07-22 17:06 447 查看
在实际应用中,我们经常用到多线程,如车站的售票系统,车站的各个售票口相当于各个线程。当我们做这个系统的时候可能会想到两种方式来实现,继承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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: