01 基本概念
并发VS并行
并行:指同一时刻,有多条指令再多个处理器上同时执行,所以无论时从微观还是从宏观来看,二者都是一起执行的。
并发:指再同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行,使得再宏观上具有多个进程同时执行的效果,但再微观上并不是同时执行的,只是把时间分成若干段,使多个进程快速交替的执行。
举个例子:
田径运动中的个人赛和接力赛。
线程VS进程
进程:进程是指可执行程序存放再计算机存储空间的一个指令序列,它是可动态,重复执行的过程。同时进程也是计算机多任务操作系统为任务分配资源的最小单位,每一个进程都应该有自己的内存空间。
线程:线程从某种角度来说,它和进程一样,也是指令序列动态,重复执行的过程。线程依赖于进程,线程随着进程消亡而消亡。线程也是多任务操作系统用于分配计算机CPU时间的最小单位。
上下文
线程执行过程中某一时间点CPU寄存器和程序计数器的内容
线程的生命周期
new - running -dead -runnable -block
xss-线程的堆栈大小 512kb
新建状态(NEW)
新建状态(NEW) 当程序使用new关键字创建了一个线程之后,该线程就处于新建状态,此时仅由JVM为其分配内存,并初始化其成员变量的值。
就绪状态(RUNNABLE)
就绪状态(RUNNABLE) 当线程对象调用了start()方法之后,该线程处于就绪状态。Java虚拟机会为其创建方法调用栈和程序计数器,等待调度运行。
运行状态(RUNNING)
运行状态(RUNNING) 如果处于就绪状态的线程获得了CPU,开始执行run()方法的线程执行体,则该线程处于运行状态。
阻塞状态(BLOCKED)
阻塞状态(BLOCKED)阻塞状态是指线程因为某种原因放弃了cpu使用权,也即让出了cpu timeslice,暂时停止运行。直到线程进入了可运行(RUNABLE)状态,才有机会再次获得cpu timeslice转到运行(running)状态。
等待阻塞:运行中的线程执行wait方法,线程进入等待队列。
同步阻塞:运行中线程获得对象锁时,若锁被占用,线程进入锁池。
其他阻塞:运行中线程执行sleep,join方法,或者发生IO请求时,当sleep超时,join等待线程超时或者终止,IO处理完毕,线程转入就绪状态。
线程死亡(DEAD)
结束后就是死亡状态。
正常结束:线程逻辑执行结束。
异常结束:运行过程中抛出未捕获的异常或错误。
调用stop:直接调用stop方法结束线程,该方法可能造成数据丢失及死锁问题,不推荐使用。
线程调度模型
1. 抢占式调度
每个线程将由系统来分配执行时间,线程的切换不由线程本身来决定,JVM采用的线程调度模型为抢占式调度。
2.协同式调度
协同式线程调度,线程执行时间由线程本身来控制,线程把自己的工作执行完后,要主动通知系统切换到例外一个线程上,最大的好处就是实现简单,且切换操作对线程自己时可知的,没啥线程同步问题。坏处时线程的执行时间不可控制,如果一个线程由问题,可能一直阻塞再那里。
线程安全
同一进程中不同的线程并行更新公共资源(临界资源)时,需要对参与的线程进行按照特定规则的进行控制,来保证公共资源不会出现污染等意外情况。
保证线程安全的方式:
synchronized
lock
atomic
线程池
频繁创建和销毁线程带来的性能上的损耗,特别是并发请求数量大的情况下,可能系统在创建和销毁线程过程中耗费的系统资源要比处理实际请求所消耗的资源还要多的多,这是我们无法接受的.线程池在这个时候应运而生,它为线程生命周期的性能开销和资源不足问题提供了解决方案.那使用线程池究竟能有什么好处呢?
1、降低资源消耗;
2、保障系统稳定;
3、某种程度上来说能提高系统响应速度;
Java 里面线程池的顶级接口是 Executor,但是严格意义上讲 Executor 并不是一个线程池,而
只是一个执行线程的工具,真正的线程池接口是 ExecutorService。
Executors.newCachedThreadPool 可根据需要创建新线程,适用于短期异步任务场景
Executors.newFixedThreadPool 可重用固定线程数的线程池,高并发场景需要注意OOM
Executors.newScheduledThreadPool 延后或定期执行指定任务,某些场景可取代quartz
Executors.newSingleThreadExecutor 单线程池,适用于后台线程运行期间做只做特定的逻辑