3.用Thead子类及Runnable接口类实现车站购票的一个场景(static关键字)

如上图所示,我们这里模拟一下去车站买票的情形:这里有3个柜台同时售票,总共是1000张票,这三个柜台同时买票,但是只能一个柜台卖同一张票,也就是说1号票卖了之后我们就只能买2号票,2号票卖了之后我们只能买3号票!依次类推!直到卖完这1000张票,然后再从头开始卖票!如下代码演示:
package com.bawei.multithread;
//这里模拟售票窗口
public class TicketWindow extends Thread {
//柜台名称,表示第几个柜台
private final String name;
//定义一个变量,模拟最多有这1000张票,这里之所以要用static关键字,主要是因
//为不想在线程创建对象的时候,每个线程都有一份自己的实例变量,而是想让我们这里所
//有的线程懂共享同一份数据,也就是只让这个变量初始化一次,所以这里设置为静态变量
private static final int MAX=1000;
//给定一个初始化变量,从第一张票开始卖,static同上
private static int index =1; public TicketWindow(String name) {
this.name = name;
} @Override
public void run() {
while(index <= MAX){
System.out.println("当前柜台是"+name+", 所售号码是"+(index++));
}
} }
上面这个类是Thead的子类,模拟售卖票的窗口,这里面控制着唯一资源票,而且这个票是被几个窗口所共享的,所以这里我们定义成了static的!
package com.bawei.multithread;
public class Station {
	public static void main(String[] args) {
		//这里我们创建了三个线程,来模拟3个柜台!
		TicketWindow  ticketWindow1 = new TicketWindow("1号柜台");
		ticketWindow1.start();
		TicketWindow  ticketWindow2 = new TicketWindow("2号柜台");
		ticketWindow2.start();
		TicketWindow  ticketWindow3 = new TicketWindow("3号柜台");
		ticketWindow3.start();
	}
}
这个类就是车站类,在这个类中我们创建了3个TicketWindow对象,用这三个对象来模拟3个售票窗口同时售票的情形!
注意:这里有一个问题,就是票是static的,是被多个线程对象所共享的,资源是唯一的,但是static修饰变量的生命周期是很长的,因为它不是随着类的消亡而消亡,这个变量一旦被JVM加载到内存中,它是有一个单独的区域来存放这个变量的,它的生命周期比类的生命周期可能还要长!当然这这只是一个粗糙版本的售票系统!我们下面会用代码给大家演示,如何友好的,将线程和业务分离开来的一个方式!也就是将我们的业务数据MAX,index与线程的逻辑(run方法)分离开来!那我们如何将我们的业务数据MAX、index,业务的控制逻辑语句和线程的执行单元[run方法]抽离开来呢?这时候就是Runnable接口起到一个作用,如下代码所示:我们先用线程类将业务数据和业务逻辑都封装在了这个线程类中
package com.bawei.multithread;
//线程类实现了Runnable接口,该线程类将业务数据和业务逻辑都封装在了这个线程类中!
public class TicketWindowRunnable implements Runnable {
//业务数据
private int index = 1;
private final int MAX=1000; @Override
public void run() {
//业务逻辑:操作业务数据的逻辑代码
while(index <= MAX){
System.out.println(Thread.currentThread()+"运行的号码是"+(index++));
try {
Thread.sleep(1L);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} }
如下是创建线程,并使用我们的业务数据和业务逻辑!
public class BankVersion2 {
	public static void main(String[] args) {
//		final类不能被继承,没有子类,final类中的方法默认是final的。
//		final方法不能被子类的方法覆盖,但可以被继承。
//		final成员变量表示常量,只能被赋值一次,赋值后值不再改变。
//		final不能用于修饰构造方法。
//		注意:父类的private成员方法是不能被子类方法覆盖的,因此private类型的方法默认是final类型的。
		final TicketWindowRunnable ticketWindow= new TicketWindowRunnable();
		/**
		 *上面的ticketWindow这个业务数据或者 业务逻辑的实例只有一个就是这个ticketWindow,这样就不会像上面的那个
		 *案例那样只有new了一个Thread类就new了一份自身的业务数据【当然我们通过静态变量的方式解决了这个问题,但是该方案始终不怎么好】,
		 *而这里通过我们这样的抽取,就将业务数据MAX,index和业务逻辑【run方法中操作MAX和index的代码逻辑】给抽取出来了,
		 *我们下面无论定义多少个线程,都不再会将业务数据、业务逻辑和线程控制的代码混淆在一起,也就是说业务数据、业务逻辑此时和
		 *Thread类的代码是在不同的object【对象】中的!
		 */
		Thread windowThread1 = new Thread(ticketWindow,"1号窗口");
		Thread windowThread2 = new Thread(ticketWindow,"2号窗口");
		Thread windowThread3 = new Thread(ticketWindow,"3号窗口");
		//启动这3个线程!
		windowThread1.start();
		windowThread2.start();
		windowThread3.start();
	}
}
提醒: 这里我们既然访问的业务数据是同一份,而且还要去改变它,那这里实际上是有一个线程的安全的问题,!这里我们先不讲解,先不用去管,后面我们会讲!
Runnable的中文解释就是可执行的,也就是说在这个Runnable接口实现类封装可执行单元【业务数据和业务逻辑】,这样就可以将可执行的逻辑单元和线程控制分离开来,这也是面向对象思想的一个很好体现!这与我们多线程设计模式中的哪种模式比较相近呢?答:策略模式!
创建多线程的两种方式我们这里都给大家讲到了,一种是继承Thread类的方式,另一种是实现Runnable接口的方式,实现Runnable接口的这种方式容易将业务数据和业务逻辑与线程类对象分离开来!而继承Thread类的方式却无法实现分离,new一个Thread子类就会出现一个新对象,这个对象的本身即是业务数据和业务逻辑对象同时也是线程对象!
策略模式在Thread和Runnable中的应用分析
程序最开始可以是:
package com.bawei.multithread;
//税务计算器
public class TaxCalculator {
//工资
private final double salary;
//奖金
private final double bonus;
//通过构造方法给这两个属性赋值!
public TaxCalculator(double salary, double bonus) {
super();
this.salary = salary;
this.bonus = bonus;
} //计算税务问题,在这里我们什么也没做,留给子类去实现或者去写适合他自己的代码,这里写税率的逻辑计算!
public double calcTax(){
return 0.0d;
} /**
* 也就是税率的计算我们是交给本类中的calculate()方法,当然在calculate方法内,在return this.calcTax();
* 这段代码的前后我们也可以加一些其它代码!具体的计算逻辑在这个方法中是不管的,我们交给本类的calcTax()方法!而本
* 类的calcTax()方法实际上也是个空实现,也就是什么也没做,那么就要求交给子类来实现自己的业务计算逻辑了,我们在外边
* 用本类的子类覆写本类的calcTax()方法,然后用子类对象调用本类的calculate方法,然后calculate方法帮我们调用
* 子类的calcTax()方法!
*/
public double calculate(){
return this.calcTax();
} public double getSalary() {
return salary;
} public double getBonus() {
return bonus;
} }
然后我们在别的类中调用使用该类的子类对象执行calculate()方法!
package com.bawei.multithread;
public class TaxCalculatorMain {
	public static void main(String[] args) {
		TaxCalculator taxCalculator = new TaxCalculator(10000d,2000d){
			//匿名子类覆写里面的calcTax方法,实现适合自己的业务逻辑【即税务问题】
			//这里我们的税务是工资的10%和奖金的15%
			//但是假如我这里的计算逻辑比较复杂的话,那么实际上
			@Override
			public double calcTax() {
				return getSalary()*0.1+getBonus()*0.15;
			}
		};
		double tax = taxCalculator.calculate();
		System.out.println(tax);
	}
}
程序是到这里肯定是没问题的,这样也可以,但是我们这里实际上是将税率的逻辑calTax()和税率的计算calculate()放到一起的,即都是在TaxCalculator类的子类中完成的!如果我们想将税率的计算calculate放到一个专门的接口中,那怎么实现呢?
我们只需要再定义一个接口CalculatorStrategy:
package com.bawei.multithread;
public interface CalculatorStrategy {
double calculate(double salary, double bonus);
}
然后,将我们原来TaxCalculator类修改为:
package com.bawei.multithread;
//税务计算器
public class TaxCalculator {
//工资
private final double salary;
//奖金
private final double bonus;
//计算策略
private CalculatorStrategy calculatorStrategy;
//通过构造方法给这两个属性赋值!
public TaxCalculator(double salary, double bonus) {
super();
this.salary = salary;
this.bonus = bonus;
} //计算税务问题,在这里我们什么也没做,留给子类去实现或者去写适合他自己的代码,这里写税率的逻辑计算!
public double calcTax(){
//这里你怎么计算我不管,我只需要调用这个接口的子类的calculate方法就可以了!
return calculatorStrategy.calculate(salary, bonus);
} public double calculate(){
return this.calcTax();
}
public double getSalary() {
return salary;
} public double getBonus() {
return bonus;
} public CalculatorStrategy getCalculatorStrategy() {
return calculatorStrategy;
}
//提供set方法可以将这个calculatorStrategy对象给注入进来!
public void setCalculatorStrategy(CalculatorStrategy calculatorStrategy) {
this.calculatorStrategy = calculatorStrategy;
} }
然后再定义一个CalculatorStrategy接口的一个子类实现SimpleCalculatorStrategy,如下所示:
package com.bawei.multithread;
//CalculatorStrategy接口的子类实现!
public class SimpleCalculatorStrategy implements CalculatorStrategy {
//常量要么定义成可配置的,要么就抽取出来作为常量处理!
//工资税率
private final static double SALARY_RATE = 0.1;
//奖金税率
private final static double BONUS_RATE = 0.1; //计算
@Override
public double calculate(double salary, double bonus) {
return salary*SALARY_RATE+bonus*BONUS_RATE;
} }
将原来的TaxCalculatorMain类改为:
package com.bawei.multithread;
public class TaxCalculatorMain {
	public static void main(String[] args) {
		TaxCalculator taxCalculator = new TaxCalculator(10000d,2000d);
		CalculatorStrategy strategy = new SimpleCalculatorStrategy();
		taxCalculator.setCalculatorStrategy(strategy);
		System.out.println(taxCalculator.calculate());
	}
}
如果此时我们的税率发生了变化,那么此时我们只需要将这个CalculatorStrategy接口的实现类SimpleCalculatorStrategy给替换掉就可以了!而TaxCalculator类不用变,CalculatorStrategy这个接口也不用变!当然这里我们在TaxCalculator类中是通过set方法将CalculatorStrategy设置进去的,实际上我们也可以不用这个set方法,而是使
用在构造方法中传入该CalculatorStrategy类型的子类对象即可!
实际上我们是借助上面的这个CalculatorStrategy接口给大家说明Runnable接口也是这么分离出来的,而且Thread类也是通过构造器注入的这个Runnable接口的实现类!
其实Runnable准确的说并不算一个线程,它只是Thread中用到的一个可运行单元,它里面有一个run方法,Thread会调用这个run方法,Thread类是在start0()这个c++代码
中调用的Runnable接口的run方法!这里Thread线程类本身实现了Runnable接口, 由源码可以看出,如果我们在创建Thread对象的时候不给对象传递Runnable接口子类对象,那么Thread类就不会调用Runnable接口子类的run方法,如果创建Thread子类对象的时候在构造器中给它传递了Runnable接口子类对象,那么Thread子类就会调用这个Runnable接口子类对象中的run方法!
总结:我们讲了多线程的两种实现方式,继承Thread类,实现Runnable接口,Runnable接口的好处【将业务数据、业务逻辑【可执行单元】和线程分离开来】,以及Runnable接口是一个什么样的设计思想【策略模式】,
3.用Thead子类及Runnable接口类实现车站购票的一个场景(static关键字)的更多相关文章
- 详解~实现Runnable方法创建线程之为什么要将Runnable接口的子类对象传递给Thread的构造函数
		
/** * @author zhao * @TIME 0419 22:56 End *定义线程的第二种方法:实现Runnable接口(不考虑安全问题) *步骤:1,定义一个子类实现Runnable接口 ...
 - 探Java多线程Thread类和Runnable接口之间的联系
		
首先复习一下Java多线程实现机制,Java实现多线程方法有如下这么几种: 1.继承了(extends)Thread类 2.实现了(implements)Runnable接口 也就是说 有如下两种情 ...
 - Java实现线程的两种方式?Thread类实现了Runnable接口吗?
		
Thread类实现了Runnable接口吗? 我们看看源码中对与Thread类的部分声明 public class Thread implements Runnable { /* Make sure ...
 - Java知多少(58)线程Runnable接口和Thread类详解
		
大多数情况,通过实例化一个Thread对象来创建一个线程.Java定义了两种方式: 实现Runnable 接口: 可以继承Thread类. 下面的两小节依次介绍了每一种方式. 实现Runnable接口 ...
 - 线程--实现Runnable接口
		
实现Runnable接口,创建线程步骤: 1.定义类,并实现Runnable接口 2.重写Runnable接口中的run()方法 3.通过Thread类建立线程对象 4.将实现了Runnable接口的 ...
 - java中Runnable接口含义
		
Java中实现多线程有两种途径:继承Thread类或者实现Runnable接口. Runnable接口非常简单,就定义了一个方法run(),继承Runnable并实现这个 方法就可以实现多线程了,但是 ...
 - java.lang.Runnable接口
		
大家都知道使用线程的2种方式,一是继承Thread类,二是实现Runnable接口.实际上,即使你实现了Runnable接口,终于还是要构造一个Thread类的对象.看过Thread源码发现,事实上这 ...
 - php 抽象类和接口类
		
PHP中抽象类和接口类都是特殊类,通常配合面向对象的多态性一起使用. 相同: ①两者都是抽象类,都不能实例化. ②只有接口类的实现类和抽象类的子类实现了 已经声明的 抽象方法才能被实例化. 不同: ① ...
 - c++ 接口类
		
什么是接口类?2017-06-07 接口类就是只提供接口不提供实现的类,就是接口类,接口类和抽象类对C++而言,没有什么区别. 接口类有如下特点: 子类来实现接口类中没有实现的所有接口. 接口方法前面 ...
 
随机推荐
- 查找->动态查找表->哈希表
			
文字描述 哈希表定义 在前面讨论的各种查找算法中,都是建立在“比较”的基础上.记录的关键字和记录在结构中的相对位置不存在确定的关系,查找的效率依赖于查找过程中所进行的比较次数.而理想的情况是希望不经过 ...
 - Python开发【笔记】:接口压力测试
			
接口压力测试脚本 1.单进程多线程模式 # #!/usr/bin/env python # # -*- coding:utf-8 -*- import time import logging impo ...
 - B+树vs. LSM树(转)
			
原文:<大型网站技术架构:核心原理与案例分析>,作者:李智慧 本书前面提到,由于传统的机械磁盘具有快速顺序读写.慢速随机读写的访问特性,这个特性对磁盘存储结构和算法的选择影响甚大. 为了改 ...
 - es的scoll滚动查询技术
			
如果一次性要查出来比如10万条数据,那么性能会很差,此时一般会采取用scoll滚动查询,一批一批的查,直到所有数据都查询完处理完 使用scoll滚动搜索,可以先搜索一批数据,然后下次再搜索一批数据,以 ...
 - 如何快速REPAIR TABLE
			
早上到公司,刚准备吃早餐,手机响了,一看是服务器自动重启了.好吧,准备修复数据吧.游戏服的游戏日志使用的是MyISAM.众所周知,MyISAM表在服务器意外宕机或者mysqld进程挂掉以后,MyISA ...
 - (1.8)mysql中的trace
			
(1.8)mysql中的trace 以json格式存储
 - Visual Studio 10.0设置引用HalconDotNet.dll
			
版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/u010435562/article/details/8858638 開始做Halcon的上位机.选用 ...
 - 并发编程---IO模型
			
IO模型 任务的提交方式有两种: 同步:应用程序提交完任务,等待结果结果,之后在执行下一个任务 异步:应用程序提交完任务,继续执行不等待结果,任务执行完,会自动出发异步中的会带哦函数 同步不等于阻塞: ...
 - 类属性"get"必须声明主体,因为它未标记为 abstract 或 extern[解决方法]
			
当在页面cs文件中,写类属性时,运行会碰到以下问题:CS0501: “ASP.default_aspx.Person.Level.get”必须声明主体,因为它未标记为 abstract 或 exter ...
 - 几种Robust方法对比
			
1.Mean Absolute Deviation http://mathbits.com/MathBits/TISection/Statistics1/MAD.html 2.Median Absol ...