本篇主要介绍一下在 Java 语言中,对于多线程的理解、使用与总结。
一 简介![](https://oscimg.oschina.net/oscnet/ce43e1877c407c970e2e77adb23d4f61487.jpg)
通常线程的概念可以这样理解:
1 客户端----> 服务端;
2 客户端发起一个请求,服务端会启动一个线程去处理这个请求,这个线程先称为主线程;
3 多个客户端发起请求时,服务端会针对每个客户端的请求启动一个线程,去处理;
4 服务端启动的这个主线程,会顺序处理相应的业务逻辑;(主要讲述一下这个阶段)
.......
如图所示:
主线程在处理业务逻辑时,是按照顺序执行的。像图中所示,整个主线程的处理时间是 4秒。这样的请求时间,简直不能忍受。
想想我们现实中的业务场景:
1 BS客户端请求数据;
2 SS服务间的调用;
这个时候我们可以使用多线程去处理这样的问题,处理方案:
1 启用多个子线程同时处理,合并每个子线程的处理结果;
例如上图场景,启动三个子线程,子线程 1处理任务1,子线程 2处理任务2,子线程 3处理任务3,每个子线程处理完后,都把处理结果添加到结果集中;
并行处理应根据具体的业务场景实现;
2 主线程只做接收参数处理,异步线程处理业务逻辑,完成后采用其他形式通知请求方;
例如上图场景,先做接收成功xiang响应;启动一个异步线程,去处理业务逻辑 任务1、任务2、任务3,处理完成后,通知客户端处理结果、或者根据标识让客户端来取结果等。
二 多线程实现方式
实现子线程或异步线程的方式通常有三种:实现Runnable接口,继承Thread类,实现Callable接口;
1 继承 Thread 类
该方式 无返回值,但是可以通过共现结果变量、标记的形式实现;
样例:
package com.study.thread; import lombok.extern.slf4j.Slf4j; import java.util.LinkedList; import java.util.List; /** * 继承 thread 类的方式实现多线程 * @auth zhangmj * @date 2019/2/11 14:45 */ @Slf4j public class ExampleThread extends Thread { private final static long SLEEP_TIME = 2000l; private String name; private String code; public ExampleThread(String name, String code) { this.name = name; this.code = code; } @Override public void run() { // run 方法内处理业务逻辑 try { log.debug(" 继承 Thread 类实现多线程,开始......"); log.debug("{}:{}", name, code); Thread.currentThread().sleep(SLEEP_TIME); log.debug("执行业务逻辑啦......"); log.debug("......*......"); log.debug(".....***....."); log.debug("....*****...."); log.debug("...*******..."); log.debug("......*......"); log.debug("{}:{}", name, code); log.debug(" 继承 Thread 类实现多线程,结束......"); } catch (InterruptedException e) { log.error("线程内部错误", e); } } public static void main(String[] args) { List<String> list = new LinkedList<String>(); ExampleThread exampleThreadOne = new ExampleThread("样例线程 1","THREAD---0001"); exampleThreadOne.start(); ExampleThread exampleThreadTwo = new ExampleThread("样例线程 2","THREAD---0002"); exampleThreadTwo.start(); } }
2 实现 Runnable 接口
该方式与继承Thread类类似,无返回值,但是可以通过共现结果变量、标记的形式实现;
样例:
package com.study.thread; import lombok.extern.slf4j.Slf4j; /** * @auth zhangmj * @date 2019/2/11 14:51 */ @Slf4j public class ExampleRunnable implements Runnable { private final static long SLEEP_TIME = 2000l; private String name; private String code; public ExampleRunnable(String name, String code) { this.name = name; this.code = code; } @Override public void run() { // run 方法内处理业务逻辑 try { log.debug(" 实现 Runnable 接口实现多线程,开始......"); log.debug("{}:{}", name, code); Thread.currentThread().sleep(SLEEP_TIME); log.debug("执行业务逻辑啦......"); log.debug(".......❤......"); log.debug(".....❤❤❤....."); log.debug("....❤❤❤❤...."); log.debug("...❤❤❤❤❤..."); log.debug("..❤❤❤❤❤❤❤......"); log.debug("{}:{}", name, code); log.debug(" 实现 Runnable 接口实现多线程,结束......"); } catch (InterruptedException e) { log.error("线程内部错误", e); } } public static void main(String[] args) { ExampleRunnable exampleRunnableOne = new ExampleRunnable("Runnable 样例线程 1","Runnable---0001"); Thread threadOne = new Thread(exampleRunnableOne); threadOne.start(); ExampleRunnable exampleRunnableTwo = new ExampleRunnable("Runnable 样例线程 2","Runnable---0002"); Thread threadTwo = new Thread(exampleRunnableTwo); threadTwo.start(); } }
3 实现 Callable接口
该方式有返回值,实现方式与前两种有所不同,需要 实现类FutureTask配合使用;
样例:
package com.study.thread; import lombok.extern.slf4j.Slf4j; import java.util.LinkedList; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; /** * @auth zhangmj * @date 2019/2/11 14:56 */ @Slf4j public class ExampleCallable implements Callable<String> { private final static long SLEEP_TIME = 2000l; private String name; private String code; public ExampleCallable(String name, String code) { this.name = name; this.code = code; } @Override public String call() throws Exception { // run 方法内处理业务逻辑 try { log.debug(" 实现 Callable 接口实现多线程,开始......"); log.debug("{}:{}", name, code); Thread.currentThread().sleep(SLEEP_TIME); log.debug("执行业务逻辑啦......"); log.debug(".......卍......"); log.debug(".....卍卍卍....."); log.debug("....卍卍卍卍...."); log.debug("...卍卍卍卍卍..."); log.debug("..卍卍卍卍卍卍卍......"); log.debug("{}:{}", name, code); log.debug(" 实现 Callable 接口实现多线程,结束......"); StringBuffer sb = new StringBuffer(); sb.append("name:").append(name).append(" code:").append(code); return sb.toString(); } catch (InterruptedException e) { log.error("线程内部错误", e); } return null; } public static void main(String[] args) throws Exception { List<FutureTask<String>> taskList = new LinkedList<FutureTask<String>>(); ExampleCallable exampleCallableOne = new ExampleCallable("Callable 样例线程 1","Callable---0001"); FutureTask<String> taskOne = new FutureTask<>(exampleCallableOne); taskList.add(taskOne); Thread threadOne = new Thread(taskOne); threadOne.start(); ExampleCallable exampleCallableTwo = new ExampleCallable("Callable 样例线程 2","Callable---0002"); FutureTask<String> taskTwo = new FutureTask<>(exampleCallableTwo); taskList.add(taskTwo); Thread threadTwo = new Thread(taskTwo); threadTwo.start(); // 打印返回结果 for (FutureTask<String> futureTask : taskList) { log.debug("返回结果:{}", futureTask.get()); } } }
三 总结
以上三种实现方式,都是采用直接 新建线程实例的方式实现的。
在正常的业务实现中,可以实现使用,但是会有一定效率降低,通常会使用线程池的方式。