《深入理解java虚拟机:jvm高级特性与最佳实践》笔记——Java线程和锁

a) 线程实现:thread类的所有关键方法都是native的,意味着平台相关。
i. 使用内核线程(KLT直接由操作系统内核支持的线程)实现:内核通过调度器对线程进行调度,并负责将线程任务映射到各个处理器上。程序使用轻量级进程(LWP,一种内核线程的高级接口,即程序线程),这种内核线程与轻量级进程之间1:1的关系成为一对一的线程模型。【局限性】:进程操作需要进行系统调用,代价高,在用户态和内核态来回切换;内线资源有限,支持的线程有限。
《深入理解java虚拟机:jvm高级特性与最佳实践》笔记——Java线程和锁_第1张图片
ii. 使用用户线程实现:狭义用户线程完全建立在用户空间线程库中,系统不能感知,所有的操作均在用户态中完成,因此操作快速而且低消耗,支持规模大。这种进程与用户线程之间1:N的关系称为一对多的线程模型。【局限性】:实现空难,需要用户程序自己处理实现机制和错误处理等问题。
《深入理解java虚拟机:jvm高级特性与最佳实践》笔记——Java线程和锁_第2张图片
iii. 混合实现:用户线程仍完全建立在用户空间,而操作系统提供的轻量级进程则作为用户线程和内核线程之间的桥梁,内核线程提供线程调度及处理器映射等功能。用户线程与内核线程是m:n的关系,即多对多的线程模型。
《深入理解java虚拟机:jvm高级特性与最佳实践》笔记——Java线程和锁_第3张图片
v. Java线程的实现:与平台有关,不同的平台实现不一样
vi. Java线程的调度:

  1. 协同式:线程执行时间有线程本身控制,线程执行完成后,通知系统切换线程。实现简单,切换操作可知,无同步问题。执行时间不可控,阻塞问题。
  2. 抢占式:系统分配执行时间,切换由系统决定。线程执行时间系统可控,无阻塞问题。线程优先级不能保证其优先处理,会被系统优化掉。
    b) 线程状态:
    《深入理解java虚拟机:jvm高级特性与最佳实践》笔记——Java线程和锁_第4张图片
    i. 新建(new):创建后尚未启动的线程
    ii. 运行(runnable):包括了系统线程状态的running和ready,处于执行中或者等待分配执行时间
    iii. 无限期等待(waiting):系统不会分配执行时间,等待其他线程主动唤醒。没有设置timeout参数的object.wait()和thread.join(),以及lockSupport.park();
    iv. 限期等待(timed waiting):不会分配执行时间,无需其他线程唤醒,系统会自动唤醒。
    v. 阻塞(blocked):进程被阻塞。等待另一个线程释放线程锁时发生,等待则是程序等待进入同步区域的时候发生。
    vi. 结束(terminated):已终止线程的状态,线程已经执行结束。
    15、 线程安全:当多个线程访问一个对象时,如果不用考虑这些线程运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那么这个对象就是线程安全的。
    a) Java中的线程安全:
    i. 不可变对象:不可变对象(只要是被正确的构建出来,没有发生this引用逃逸)一定是线程安全的。
    ii. 绝对线程安全:java API 中标注自己是线程安全的类,大多数都不是绝对的线程安全,即使被synchronized关键字修饰所有方法,在使用时仍然需要同步手段。
    iii. 相对线程安全:保证对一个对象单独的操作是线程安全的。
    iv. 线程兼容:对象本身不是线程安全的,但是可以通过在调用端正确的使用同步手段来保证对象在并发环境内安全使用。
    v. 线程对立:无论是否采取了同步措施,都无法在多线程环境中并发使用。
    b) 线程安全实现方法:
    i. 互斥同步:多个线程并发访问共享数据时,保证共享数据在同一时刻只被一条线程使用。临界量、互斥量和信号量是主要实现方式。进行线程阻塞和唤醒会带来性能问题。悲观并发策略。
  3. Synchronized关键字:执行monitorenter时,首先获取所,如果没有被锁定,或者当前线程已经有了锁,则将锁计数器加1,执行monitorexit时,计数器减1,当为0时,锁被释放。如果获取锁失败,则阻塞等待。可重入锁,线程执行完前会阻塞其他线程。线程操作由操作系统完成,耗时长。
  4. ReentrantLock:java语法层面的互斥锁。
    a) 等待中断:等待线程在长时间获取不到锁,可以主动放弃,改处理其他事情。
    b) 公平锁:多个线程同时等待时,必须按照时间顺序来依次获取锁。
    c) 锁绑定多个条件:通过newCondition()方法绑定多个条件。
    ii. 非阻塞同步:基于冲突检测的乐观并发策略,优先进行操作,然后进行数据同步,如果产生冲突,则进行补偿,无需挂起线程。硬件指令实现:
  5. 测试并设置(Test-and-Set)指令:
  6. 获取并增加(Fetch-and-Increment)指令:
  7. 交换(Swap)指令:
  8. 比较并交换(Compare-and-Swap , CAS)指令:CAS需要三个操作数,分别是内存位置V,旧的预期值A和新值B,CAS指令执行时,当且仅当V符合A时,处理器会用B更新V的值,否则更新失败。是一个原子操作。但存在ABA问题(如果V初次和赋值检查时读的都是A值,但在这期间别改到B值又被改回A值,CAS会误认为没有改变过)。
  9. 加载链接/条件储存(Load-Linked/Store-Conditional ,LL/SC)指令:
    iii. 无同步方案:没有因果关系,不涉及共享数据,无需同步。
  10. 可重入代码:代码在任何时候中断,执行其他代码回来重新执行,程序也不会出错的代码。不依赖于公共资源、用到的状态量都由参数中传入等
  11. 线程本地存储:如果共享数据保证在同一个线程内,也无需同步。可以通过threadLocal类(通过threadLocalHashCode为k,本地线程变量为v的k-v值对)来实现本地储存功能。
    16、 锁优化:
    a) 自旋锁与自适应自旋:通过让线程执行一个忙循环,让请求线程等待一小段时间,同时不放弃处理器的执行时间。在多处理器及线程占用时间较长,自旋效果差,而且会带来性能浪费,因此自旋需要有一定限度。
    b) 锁消除:通过逃逸分析技术,虚拟机即时编译器运行时,会对代码要求同步,但被检测到不存在共享数据竞争的锁进行消除。
    c) 锁粗化:虚拟机探测到有一串零碎的操作对同一个对象加锁,将会加锁同步的范围扩展到整个操作序列的外部。
    d) 轻量级锁:在没有多线程竞争的前提下,减少传统的重量锁使用操作系统互斥量产生的性能消耗。【HotSpot虚拟机对象的内存布局】:对象头包括两部分,一部分为对象自身的运行时数据(32位和64为虚拟机中分别为32和和64个bits),比如哈希值,gc分代年龄等,官方称其为“Mark Word”,另一部分用于存储指向方法区对象类型的指针。
    《深入理解java虚拟机:jvm高级特性与最佳实践》笔记——Java线程和锁_第5张图片
    在代码进入同步块时,如果对象没有被锁定(锁标识位为“01”状态),虚拟机首先将在当前线程的栈帧中建立一个名为锁记录的空间,用于存储锁对象目前的Mark Word的拷贝。虚拟机使用CAS操作对象Mark Word更新为指向Lock Record的指针。更新成功,则拥有该锁,并将标志位转变为“00”.如果更新失败,则先检查是否指向当前线程的栈帧,如果是则执行,否则为其他线程锁定。如果多个线程争用一个锁,则膨胀为重量锁,标志位变为“10”.解锁过程依然为CAS操作。
    e) 偏向锁:消除数据在无竞争情况下的同步原语,进一步提高程序的运行性能。偏向锁在无竞争下,连CAS也不做。该锁在没有其他线程获取的情况下,则持有偏向锁的线程永远不需要同步。
    《深入理解java虚拟机:jvm高级特性与最佳实践》笔记——Java线程和锁_第6张图片

你可能感兴趣的:(《深入理解java虚拟机:jvm高级特性与最佳实践》笔记——Java线程和锁)