线程与进程
- 进程:操作系统分配资源的基本单位。
- 线程:CPU调度(执行)的基本单位。共享进程的资源。
一个CPU同一时间只能执行一个线程,表象是线程切换(Context Switch)。对CPU来说,寄存器存数、ALU计算、PC(程序计数器)记录位置。
线程切换过程
线程时间结束后,当前整个状态(PC、寄存器等)全部拿出去放到缓存或者内存中,也成为保护现场。
线程一定是越多越好吗
不,过多线程的切换需要资源较多。
JVM级别线程
Class启动的Thread,JVM级别线程,对应操作系统内核线程。
当然JVM没有规定一一对应关系,但是HotSpot实现,JVM线程:内核线程=1:1。可参见:https://www.cnblogs.com/MrSaver/p/12987454.html。还是一种线程模型是,用户级别线程,通过虚拟的PC、虚拟寄存器,管理属于用户空间的线程,轻量级的线程——协程,纤程。短计算场景下优于内核线程。
注:JDK1.2之前,Synchronized由OS帮忙管理,非常的重量级锁。
锁的本质
锁定对象,可参见:https://www.cnblogs.com/MrSaver/p/13024132.html。锁上面有等待队列,以实现调度。
CAS
这样的代码,结果一定不是一百万。注意,加Volatile没有用!
CAS(Compare And Swap),最底层是native代码,由操作系统负责实现。乐观锁,马士兵称之为自旋锁,本质死循环,但是一定能有执行成功的时候!
问1:如何处理ABA问题?
答:加版本处理。
问2:为什么会比重量级锁效率高?
答:伪命题,未必,即使经历过用户空间的优化,具体问题具体分析。
自旋是消耗CPU资源的,如果锁的时间长,或者自旋线程多,CPU会被大量消耗。重量级锁有等待队列,所有拿不到所得进入等待队列,不需要消耗CPU资源。
问3:原子性如何实现!
答:硬件指令集提供,见下代码!
进入之后,汇编命令:CMPXCHG。
JOL(Java Object Layout)与锁
对象的内存布局打印信息如下:
当我们给对象加Synchronized锁后,再打印JOL信息:
MarkWord锁内容具体如下:
偏向锁
偏向锁也是JDK1.6中引入的一项锁优化。目的是在无竞争的情况下把整个同步都消除掉,包括CAS操作。这个锁会偏向于第一个获得它的线程,如果在接下来的执行过程中,该锁没有被其他线程获取,则持有偏向锁的线程讲永远不需要进行同步。但是一旦有另外一个线程去尝试获取这个锁时,偏向模式就宣告结束,撤销偏向后恢复到未锁定(01)或轻量级锁定(00)状态。
在统计学分析下,一个锁大多数情况下只有一个线程再用,比如Vector、StringBuffer很多方法都会有Synchronized,但是通常业务下只有一个线程在用。
锁升级过程
总体路径:无锁——偏向锁——自旋锁——重量级锁。
多线程竞争下,偏向锁(放在MarkWord)撤销先变为自旋锁(CAS方式),重度竞争的情况下,线程较多的情况下,升级为重量级锁。