模拟排队等号例子
Thread:
public class TicketWindows extends Thread{
private final String name;
private final int MAX = 6;
private int index = 1;
public TicketWindows(String name) {
this.name = name;
}
@Override
public void run() {
while(index <= MAX){
System.out.println("柜台"+name+"出票:" + index++);
}
}
public static void main(String[] args) {
TicketWindows ticketWindows1 = new TicketWindows("一");
ticketWindows1.start();
TicketWindows ticketWindows2 = new TicketWindows("二");
ticketWindows2.start();
TicketWindows ticketWindows3 = new TicketWindows("三");
ticketWindows3.start();
}
}
上述代码从打印结果可以看出:每个窗口都出票了6次,而不是共同出票6次。
因为创建了3个实例,每个实例的index都是独立的。可以在index前面加static避免这种情况,下面介绍通过Runable解决。
Runable:
public class TicketWindowsRunable implements Runnable{
private String name;
private final static int MAX = 6;
private int index = 1;
@Override
public void run() {
while (index <= MAX) {
System.out.println(Thread.currentThread().getName() + "号码是" + index++);
}
}
public static void main(String[] args) {
final TicketWindowsRunable ticketWindowsRunable = new TicketWindowsRunable();
Thread thread1 = new Thread(ticketWindowsRunable, "1");
Thread thread2 = new Thread(ticketWindowsRunable, "2");
Thread thread3 = new Thread(ticketWindowsRunable, "3");
thread1.start();
thread2.start();
thread3.start();
}
}
3个窗口共同发票6次。(暂不考虑线程安全问题)
Runnable和Thread区别
实际开发中我们通常采用Runnable接口来实现多线程。因为实现Runnable接口比继承Thread类有如下好处:
- 避免继承的局限,一个类可以继承多个接口,但是类只能继承一个类。
- Runnable接口实现的线程便于资源共享。而通过Thread类实现,各自线程的资源是独立的,不方便共享。上面例子可以看出线程ticketWindows1,2,3各卖了6张票,而线程thread1,2,3共卖6张票.
使用Runnable对象时,Runnable定义的子类没有start()方法,只有Thread类中才有,观察Thread类,有一个构造方法public Thread(Runnable target),此构造方法接受Runanble的子类实例,也就是说可以通过Thread类来启动Runnable实现多线程。这种模式和**策略模式**相识,下面讲解策略模式。
策略模式
- 创建接口CalculatorStrategy
public interface CalculatorStrategy {
double calculate(double salary, double bonus);
}
- 实现接口的类SimpleCalculatorStrategy
public class SimpleCalculatorStrategy implements CalculatorStrategy {
private final static double SALARY_RATE = 0.1;
private final static double BONUS_RATE = 0.15;
@Override
public double calculate(double salary, double bonus) {
return salary * SALARY_RATE + bonus * BONUS_RATE;
}
}
- 类TaxCalaculator
public class TaxCalaculator {
private final double salary;
private final double bonus;
private final CalculatorStrategy calculatorStrategy;
public TaxCalaculator(double salary, double bonus,CalculatorStrategy calculatorStrategy) {
this.salary = salary;
this.bonus = bonus;
this.calculatorStrategy = calculatorStrategy;
}
//执行实现接口得方法
protected double calcTax(){
return calculatorStrategy.calculate(salary,bonus);
}
public double calculate(){
return this.calcTax();
}
}
- 测试mian
public static void main(String[] args) {
TaxCalaculator calculator = new TaxCalaculator(10000d, 2000d,new SimpleCalculatorStrategy());
System.out.println(calculator.calculate());
}
如此费劲到底有什么好处呢? 如果下次计算公式变了,只要重写个类去实现接口,重写方法就行了,不影响之前的代码逻辑。比如重写写类A实现接口,调用的时候TaxCalaculator calculator = new TaxCalaculator(10000d, 2000d,new A());
就ok了,是不是很Runable很相识。
注意:在java8中有更好的方法,避免了单独写类去实现接口。如:TaxCalaculator calculator = new TaxCalaculator(10000d, 2000d,(s,b)->s * 0.1 + b * 0.15);
总结:结合上一篇,我们知道了Runable和Thread利用 模板模式和 策略模式,通过学习多线程间接学习了两种设计模式,很有成就感,有木有?????