Java 多线程卖票
2016-07-22 17:06
447 查看
在实际应用中,我们经常用到多线程,如车站的售票系统,车站的各个售票口相当于各个线程。当我们做这个系统的时候可能会想到两种方式来实现,继承Thread类或实现Runnable接口,现在看一下这两种方式实现的两种结果。
运行结果:
![](http://img.blog.csdn.net/20160806110602323)
继承Thread类的,我们相当于拿出三件事即三个卖票5张的任务分别分给三个窗口,他们各做各的事各卖各的票各完成各的任务,因为MyThread继承Thread类,所以在new MyThread的时候在创建三个对象的同时创建了三个线程;
![](http://img.blog.csdn.net/20160722170056744)
运行结果
![](http://img.blog.csdn.net/20160722161451407)
实现Runnable的, 相当于是拿出一个卖票10张得任务给三个人去共同完成,new MyThread1相当于创建一个任务,然后实例化三个Thread,创建三个线程即安排三个窗口去执行。
![](http://img.blog.csdn.net/20160722170323778)
![](http://img.blog.csdn.net/20160806112055984)
多次跑会出现这种情况,还是线程不安全,思考一下,如果2 3窗口在还有1张被锁住,等待完资源,此时数据已经脏了。
this.ticket这部操作,将ticket从主内存读取到工作内存,2号 3号同时读取了2个副本,因此可能出现不安全情况。
![](http://img.blog.csdn.net/20160806114301062)
线程安全,数据正常。
1. 获得同步锁;
2. 清空工作内存;
3. 从主内存拷贝对象副本到工作内存;this.ticket到工作内存
4. 执行代码(计算或者输出等);
5. 刷新主内存数据;
6. 释放同步锁。
所以使用synchronized ,锁的位置很重要!!!
![](http://img.blog.csdn.net/20160806113156505)
线程安全,数据正常。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
继承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
相关文章推荐
- Java Annotation —— 注解基础
- 设计模式大杂烩之一
- 使用Sax生成xml文件
- 简单的改变图片大小的java工具类
- 【JAVA】内嵌JettyServer结合springMVC、mybatis实现轻量化系统部署
- spring roo 开发meavn项目(五)后台发送邮件功能
- 如何搭建Java开发环境(包括下载、安装和配置JDK)和Eclipse的安装
- forName()和loadClass()的区别
- springmvc请求参数获取的几种方法
- Eclipse中JVM内存设置
- Spring 教程
- java反射
- [6]访问控制权限 0X02
- 分析JAVA应用CPU占用过高的问题
- java UnicodeHtml
- Java中String与byte[]的转换
- java获得当前日期,或把日期转为long型的ms值
- Struts2
- eclipse中java项目的build path详解
- [java][leetcode 372]Super Pow