创建线程有两种方式: 第一种:1.1小节说过的继承Thread类重写里面的run()方法。 第二种:实现Runnable接口再实现run()方法,然后分配类的实例,在创建Thread时作为参数传递过去。

  • Runnable接口其实是一个函数式接口(Functional Interface),关于什么是 Functional Interface,这是java8里的概念。

  • 那通过 Runnable 的方式怎么去创建线程的?我不会直接说明怎么去用,说明只是很简单,我们要去延伸一下,比如这个东西的必要性在哪里?,他的原因是什么?

先做一个案例:

  • 需求: 在银行没有自助终端机时叫号,有3个柜台和屏幕,屏幕显示第几号。当有一个人办业务时首先抽一个号,手里拿着票,这个票是前面有人办理完业务之后,柜员按一下“下一位”后得到的。相当于3个线程并发去工作。比如当前线程最大的号是1,叫的的时候下一位就是2,以此类推。如果到最大值为100又从1开始。
  • 运行后除了定义的三个线程还有main线程一共4个有线程,但是现在有个问题:不是一个地方去控制这个号码而是线程各玩各的,Why?

  • 答案:CounterWindow类种定义的 index 是类自己的类变量, 定义完之后产生了各自独立的实例,所有不会共享变量形成各玩各的,常用的方法是让变量实例化一次,将

       private final int MAX = 50;             private static final int MAX = 50;
       private int index = 1;        改为       private static int index = 1;
    
  • 加上static修饰符之后new时只会实例化一个

  • 结果显示顺序混乱了,很明显线程和业务逻辑部分是混淆在一起的,这个后面再说。二号叫了个1,一号叫的时候看到的还是1要++成2,但是还没有输出之前,三号已经把2输出出来了。虽然顺序还乱,但是需求完成了,这三个人使用的是同一个叫号器。

  • 使用static修饰符貌似实现了需求,但是static有一个问题:生命周期比较长,它是伴随着JVM的启动和销毁,也就是类加载之后他就一直会在,类销毁了他也不一定销毁,因为他不是放在类的堆栈空间里的,他有一份独立的空间

  • 现在完成了一个比较粗糙的版本,那么我们来看一下把业务和线程分离出来后的版本

现在就是Runnable接口起作用的时候了:

  • 用Runnable接口将线程的逻辑执行单元从控制中抽取出来

  • 现在通过Runnable接口把业务控制的逻辑抽到了Runnable接口里面了,这样不管有定义了多少个线程,业务逻辑的实例只有一个,也就是说线程和业务逻辑控制的东西他是在不同的class,不同的Object里面,但是问题又来了:访问的数据业务单元是同一份,而且还要去改变它, 这是线程安全的问题,这个以后再说。

  • Runnable接口的作用是:将可执行的逻辑单元和线程控制分离开,这也是面向对象思想比较好的一个体现。

那Thread、Runnable与java设计模式当中的哪种比较类似? 策略模式在Thread和Runnable中的应用分析

  • 以一个计算税务的计算器为例说明

  • 插曲:国家每年对税率的算法是不一样的,如果设计一个系统的话,当这个算法调整的时候,如果设计得不好就要改很多代码去做调整,有可能会引起一些问题,如果做得比较好得话,在税率这边做点升级会非常得容易

  • 结果为1300.0,这是没问题的,但是怎么去计算和逻辑性的东西还是放全部在TaxCalculator里面,如果要改变税率的话,那么程序也将要改变,要是逻辑部分很复杂,代价就会比较大的。

  • 可以抽出一个计算税率的接口出来让他去做这个事情

  • 在定义一个具体的实现

  • 然后这个计算的方式是传递进去的

  • 如果这时候税率需要更改就只需要替换实现类就行了,如果用了Spring那改下注入就OK了

  • 这就是典型的策略模式做的事情,如果使用java8的话那就容易以了

  • 接口加上@FunctionalInterface注解

  • 实现类不变

  • new都不需要new了直接上 Lambda 表达式,使用@FunctionalInterface注解改造得更加接近一些,这样就更简洁了。

  • 前面的CounterWindow类的输出也可以改造(java8)

主要意思是:

  • 控制业务逻辑和本身算法的控制分离的一个接口,Thread用的是Runnable,上面的例子用的是ICalculativeStrategy,策略的方式分离出来,可以起到很好的抽象、单一原则等等更加OO的面向对象设计思想,这就是Runnable接口的作用。

  • Runnable接口并不是线程,他只是Thread要用到的可运行的一个逻辑单元的一个标记接口,他里面有个方法叫run()方法,Thread会在C++的那部分代码种调用run()方法。

results matching ""

    No results matching ""