目录
一.线程应用
异步调用
以调用方角度来讲,如果
- 需要等待结果返回,才能继续运行就是同步
- 不需要等待结果返回,就能继续运行就是异步
应用
- 比如在项目中,视频文件需要转换格式等操作比较费时,这时开一个新线程处理视频转换,避免阻塞主线程
- tomcat 的异步 servlet 也是类似的目的,让用户线程处理耗时较长的操作,避免阻塞 tomcat 的工作线程
- ......
并行调用
在项目开发中,经常会遇到一个问题:在一个后端接口里,往往会进行多项耗时任务(相互之间独立,没有依赖)的操作。
如:让你设计一个APP首页查询的接口,它需要查用户信息、需要查banner信息、需要查标签信息等等。一般情况,会实现如下:
public AppHeadInfoResponse queryAppHeadInfo(AppInfoReq req) {
//查用户信息
UserInfoParam userInfoParam = buildUserParam(req);
UserInfoDTO userInfoDTO = userService.queryUserInfo(userInfoParam);
//查banner信息
BannerParam bannerParam = buildBannerParam(req);
BannerDTO bannerDTO = bannerService.queryBannerInfo(bannerParam);
//查标签信息
LabelParam labelParam = buildLabelParam(req);
LabelDTO labelDTO = labelService.queryLabelInfo(labelParam);
//组装结果
return buildResponse(userInfoDTO,bannerDTO,labelDTO);
}
以上的代码实现中,查询用户、banner、标签信息,是串行的。 如果查询用户信息耗时200ms,查询banner信息100ms,查询标签信息200ms的话,耗时就是500ms了。
我们可以对这些耗时任务进行并行操作,从而使得:响应时间 约等于 耗时最大的任务处理时间,这样可以大大降低系统的响应时间,如下图所示:
注意
- 单核 cpu 下,多线程不能实际提高程序运行效率,只是为了能够在不同的任务之间切换,不同线程轮流使用cpu ,不至于一个线程总占用 cpu,别的线程没法干活
- 多核 cpu 可以并行跑多个线程,但能否提高程序运行效率还是要分情况的
- IO 操作只占用少量 cpu,只是我们一般拷贝文件使用的是【阻塞 IO】,这时相当于线程虽然只占用少量 cpu,但需要一直等待 IO 结束,没能充分利用线程。所以才有后面的【非阻塞 IO】和【异步 IO】优化
二.Java线程相关概念
进程
是程序的⼀次执行,是系统进⾏资源分配和调度的独立单位,每⼀个进程都有它自己的内存空间和系统资源
线程
线程与进程相似,但线程是一个比进程更小的执行单位。一个进程在其执行的过程中可以产生多个线程。线程是系统调度的基本单位。
管程(Monitor,监视器)
- 也就是我们平时所说的锁,Monitor其实是一种同步机制,他的义务是保证(同一时间)只有一个线程可以访问被保护的数据和代码。
- JVM中同步是基于进入和退出监视器对象(Monitor,管程对象)来实现的,每个对象实例都会有一个Monitor对象,Monitor对象会和Java对象一同创建并销毁,它底层是由C++语言来实现的。
JVM第3版描述如下:
三.用户线程和守护线程
Java线程分为用户线程和守护线程,线程的daemon属性为true表示是守护线程,false表示是用户线程。
守护线程
是一种特殊的线程,在后台默默地完成一些系统性的服务,比如垃圾回收线程。当程序中所有用户线程执行完毕之后,不管守护线程是否结束,系统都会自动退出。
用户线程
是系统的工作线程,它会完成这个程序需要完成的业务操作。
四.线程的生命周期
线程的状态
在OS中线程有5种状态,在Java中有6种,分别是NEW,RUNNABLE,WAITING, TIMED_WAITING,BLOCKED,TERMINATED,其中NEW状态是Java独有的 。
在Threa.class中定义了一个线程状态的枚举State,可以明确的看到描述。
public enum State {
/**
* 线程已创建但未启动。
*/
NEW,
/**
* 线程已在虚拟机中启动,等待操作系统调度后就可以运行。
*/
RUNNABLE,
/**
* 等待监视器锁,也就是等待进入synchronized块。
*/
BLOCKED,
/**
* 线程阻塞,等待其它线程将它唤醒,等待的方法包括Object.wait(),LockSupport.park()Thread.join()
* 另外一个线程唤醒它需要使用对应的唤醒方法,Object.notify()或Object.notifyAll(),LockSupport.unPark()
* Thread.join()会阻塞到执行join方法的线程运行结束后,才唤醒当前线程。
*/
WAITING,
/**
* 与WAITING类似,只是在Waiting的几个方法中传入等待时间的参数,没有显示的唤醒情况下,运行时间超出设置的等待时间也会唤醒。
* 此外还有Thread.sleep(long);
*/
TIMED_WAITING,
/**
* 线程执行完毕后的状态
*/
TERMINATED;
}