我们要实现一个功能,开启一个线程打印HelloWorld
先说结论
public static void main(String[] args) {
new Thread(()-> System.out.println("HelloWorld\t" + Thread.currentThread().getName()),"MyHelloWorld").start();
}
以上的代码我们实现的是:
- 开启一个线程,将该线程名称命名为MyHelloWorld
- 在线程中执行一条语句
- 语句中打印HelloWorld,同时打印线程名称
以上代码简洁,并且语义充分。使用lambda表达式简化了线程的实现,并且给线程定义了线程名称,符合java规范的要求。
接下来,我们要来研究以下几点内容
- 看看这样的实现,是怎么一步步得到的,
- 这有什么好处
实现多线程的方式
首先,我们知道实现线程的几种方式中,有两种最传统的方式。
1、继承Thread类
2、实现Runnable接口
我们来看看这两种方式在代码上是如何写的。
1、继承Thread类
下面这段代码,在打印HelloWorld的时候,同时打印了当前线程的线程名称
class HellowWorldThread extends Thread{
@Override
public void run() {
System.out.println("HelloWorld\t" + Thread.currentThread().getName());
}
}
2、实现Runnable接口
下面这段代码,在打印HelloWorld的时候,也同时打印了当前线程的线程名称
class HelloWorldRunnable implements Runnable{
@Override
public void run() {
System.out.println("HelloWorld\t"+ Thread.currentThread().getName());
}
}
代码调用
在执行的时候,我们的代码是
public static void main(String[] args) {
new HellowWorldThread().start();
new Thread(new HelloWorldRunnable()).start();
}
传统写法的缺点
- 需要额外写一个类
- 如果要给线程命名,还需要再额外提供一个有参(可以传入线程名称)的构造函数
- 业务和线程相耦合
如何优化
lambda表达式
我们知道Thread类中有个带参的构造方法,其中是需要传入一个实现Runnable接口的对象,同时传入一个线程名。
写法如下
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("HelloWorld\t" + Thread.currentThread().getName());
}
},"MyHelloWorldRunnableInnerClass").start();
}
由于Runnable是 函数式接口
因此,根据lambda表达式的改造规则,我们可以将其改造成以下的形式(就是本文开始时候的样子)
public static void main(String[] args) {
new Thread(()-> System.out.println("HelloWorld\t" + Thread.currentThread().getName()),"MyHelloWorld").start();
}
此时代码极大程度上,得到了简化。并且按照要求,还为线程提供了一个线程名称。
业务和线程耦合问题
在我们功能的实现过程中,是否需要考虑他是怎么运行的呢?我们当然可以考虑同步运行,也可以考虑并发运行。
开启一个线程,在线程中运行我们的业务代码,业务代码不需要考虑线程的问题
代码模型拆解
线程
new Thread(
//dosomthing'
).start();
业务
此时的业务代码可以以任何姿势展现,不用考虑任何多线程问题。
public void doBusiness(){
//a lot of work
}
整合
new Thread(
()-> doBusiness()
).start();
业务举例
我们要对订单进行处理,需要保存订单,通知扣减库存,通知扣费(业务上可能会更复杂)--这在OrderService中实现
我们的要多线程操作,加快处理速度。(思考OrderService需要继承Thread类吗,OrderService需要实现Runnable接口吗)
OrderService 的实现
class OrderService{
public void saveOrder(){
}
public void deductStock(){
}
public void charge(){
}
}
业务执行的多个实现方案
1、在主线程外开启一个线程,在新开启的线程中,执行所有业务
public static void order(){
OrderService orderService = new OrderService();
new Thread(
()->{
orderService.saveOrder();
orderService.deductStock();
orderService.charge();
},"NewSingleThread"
).start();
}
2、开启多个线程,保证每个线程自己独立运行
public static void order2(){
OrderService orderService = new OrderService();
new Thread(()->orderService.saveOrder(),"saveOrderThread").start();
new Thread(()->orderService.deductStock(),"deductStockThread").start();
new Thread(()->orderService.charge(),"chargeThread").start();
}
以上实现了两种方案,业务代码没有任何变动,我们还可以组合出多种多线程实现方案。
总结
我们在实现多线程的过程中,不要去继承或者实现Runnable类,使用new Thead(Runnable runnable,String threadName)去启动一个线程,其中Runnable接口则通过传入lambda表达式的方式,调用我们业务类的代码。以此做到线程实现和业务实现解耦合