wap自助建站排板,wordpress如何做站群,大学生求职创业补贴有多少钱,湖北省和建设厅网站目录
一、线程内存模型
1. 内存模型
2. 内存模型操作
二、Happens-Before原则
三、Java线程
1. 线程实现方式
2. Java线程状态
四、Java线程安全
1. 线程安全程度
2. 锁优化
五、参考资料 一、线程内存模型
1. 内存模型 内存模型主要目的是定义共享变量的访问规则共享变量如实例字段、静态字段、数组元素等线程共享变量不包含线程私有变量。内存模型中有主内存、工作内存如下图所示是两者交互关系看出主内存直接对应于物理硬件的内存而程序运行时主要访问的是工作内存。
主内存Main Memory内存模型规定所有变量都存储在主内存中工作内存Working Memory每个线程都有自己的内存且变量是主内存的副本。 线程对变量的操作只能在工作内存无法直接读写主内存线程之间变量传递必须通过主内存实现模型如下图所示。 注意内存模型与内存区域划分没有任何关系若勉强有关系则主内存对应堆对象实例工作内存对应JVM栈的部分区域。
2. 内存模型操作 内存模型操作即主内存与工作内存交互定义了8种原子性操作lock、unlock、read、load、use、assign、store、write如下表所示。 内存模型操作 特点 lock 锁定 范围主内存变量 作用把变量标识为一个线程独占的状态。 unlock 解锁 范围主内存变量 作用释放处于锁定状态的变量后才能被其他线程锁定。 read 读取 范围主内存变量 作用把变量的值从主内存传输到线程工作内存中以便后续load操作。 load 载入 范围工作内存变量 作用把read操作获取的变量值存储到工作内存的变量副本中。 use 使用 范围工作内存变量 作用把变量的值传递给执行引擎每遇到使用该变量的字节码指令。 assign 赋值 范围工作内存变量 作用把执行引擎接收到的值赋给工作内存的变量 每遇到给变量赋值的字节码指令。 store 存储 范围工作内存变量 作用把变量的值从工作内存传输到主内存中以便后续write操作。 write 写入 范围主内存变量 作用把store操作获取的变量值存储到主内存中。 注意 a.一个变量从主内存复制到工作内存必须顺序执行read、load操作但可以不连续执行 一个变量从工作内存同步到主内存必须顺序执行store、write操作但可以不连续执行 b.8种操作满足以下规则 1)read和load、store和write不允许单独出现即不会出现回写主内存但其不接受 2)不允许线程丢弃最近的assign即工作内存值改变则必须同步到主内存 3)不允许线程不原因的同步到主内存即不允许没有assign操作就同步到主内存 4)一个新变量主内存诞生即对变量进行use、store时则必须先执行assign、load 5)同一时刻只有一个线程对变量lock同一线程多次lock则必须多次unlock后才释放 6)执行lock时则必须清空工作内存中此变量的副本值后使用时重新执行load、assign 7)执行unlock之前则必须把此变量同步到主内存中 c.Java内存模型操作简化为read、write、lock、unlock的四种操作。 从上表看出内存模型主要围绕并发过程中如何处理原子性、可见性、有序性建立的这三大特性如下图所示。 注意只有一条字节码指令也不意味着是原子性解释器要运行多行代码才能实现其语义volatile修饰的变量具有特性可见性、禁止指令重排序long和double的非原子协定没有volatile修饰的64位数据的读写操作划分为两次32位操作但一般认为是原子操作概率极低。
二、Happens-Before原则 Happens-Before原则先行发生原则定义两操作之间的偏序关系因此并发安全问题不要受时间顺序影响一切按先行发生原则为准。无需任何同步手段保证先行发生规则如下表所示。 先行发生原则 特点 程序次序规则 同一个线程内按照控制流顺序在前的操作先行发生于其后的操作 管程锁定规则 释放锁操作先行发生于同一个锁的加锁操作 volatile变量规则 volatile变量写操作先行发生于读操作 线程启动规则 Thread线程start()方法先行发生于此线程的每一个动作 线程终止规则 线程中所有操作先行发生于对此线程的终止检查 线程中断规则 线程interrupt()方法调用先行发生于被中断代码检查到中断时间的发生 对象终结规则 对象构造函数完成先行发生于它的finalize()方法的开始 传递性 操作A先行发生于操作B操作B先行发生于操作C则A先行发生于C
三、Java线程
1. 线程实现方式 线程是轻量级进程各个线程共享进程资源内存地址、I/O等、又可以独立调度把一个进程的资源分配和执行调用分开。实现线程有3种方式内核线程实现1:1、用户线程实现N:1、混合实现M:N如下表所示。 线程实现 特点 内核线程实现 (1:1) 1.“内核线程”直接由OS内核完成内核完成线程切换操纵调度器对线程调度并负责将线程的任务映射到各个CPU上每个内核线程可以视为内核的一个分身 2.每个轻量级进程线程都有一个内核线程支持即1:1实现 3.缺点 a.系统调用代价大需要在用户态与内核态来回切换 b.OS支持轻量级进程的数量有限 4.Java线程采用内核线程实现。 用户线程实现 (N:1) 1.“用户线程”线程非内核线程线程的创建、同步、销毁及调度在用户态中完成无需内核的帮助映射到一个CPU上即N:1实现 2.优点无需切换到内核因此速度快、低耗更大规模的线程数 缺点线程调度实现复杂增大线程被阻塞的风险。 混合实现 (M:N) 内核和用户线程混合使用用户负责线程的创建、同步、销毁内核线程负责线程调度。 Java线程实现方式采用内核线程实现每一个java线程都直接映射到一个内核线程上HotSpot不会干涉线程的调度。 线程调度Scheduler是指线程分配处理器使用权的过程两种调度方式协同式协程、抢占式java采用如下表所示。 实现方式 特点 协同式线程调度 - 协程 (Cooperative Threads-Scheduling) 1.线程执行时间由线程本身控制线程工作执行完后主动通知系统切换到另外的线程上 2.优点切换操作对线程可知实现简单 缺点线程执行时间不可控若代码有问题则一直阻塞 3.应用Lua语言的“协同例程”。 抢占式线程调度 - Java采用 (Preemptive Threads-Scheduling) 1.线程执行时间由系统来分配执行时间如Thread::yeild()方法可以主动让出时间但无法主动获取执行时间 2.通过线程优先级可以“建议”OS多分配执行时间但是不能稳定最终还是OS决定。 Java线程调度方式采用抢占式线程调度因此Java中不能通过线程优先级完全准确判定一组Ready状态的线程会先执行哪一个。而Thread类大部分API都是Native修饰而Native往往是该方法没有使用或无法使用平台无关的手段来实现。
2. Java线程状态 Java线程状态有6种状态新建、运行Runnable Running Ready、无限期等待、限期等待、阻塞、结束如下表所示。 线程状态 特点 新建 New 创建后但尚未启动即new之后start()之前 运行 Running Ready 包含两种状态正在运行Running、正在等待系统分配执行时间Ready 无限期等待 Waiting 1.处于线程不会被分配处理器执行时间需被其他线程显示唤醒 2.方法有 没有设置timeout参数的Object::wait()若加锁会释放锁; 没有设置timeout参数的Object::join(); LockSupport::park()。 限期等待 Timed Waiting 1.处于线程不会被分配处理器执行时间无需被其他线程显示唤醒在一定时间之后系统会自动唤醒 2.方法有 Thread::sleep()若加锁不会释放锁 设置timeout参数的Object::wait()若加锁会释放锁 设置timeout参数的Object::join() LockSupport::parkNanos()、LockSupport::parkUntil()。 阻塞 Blocked 1.处于线程被阻塞需等待获取排他锁 2.“阻塞状态”与“等待状态”的区别 阻塞状态需等待获取排他锁建立在另一线程释放锁之上 等待状态等待一段时间或唤醒动作的发生。 结束 Terminated 已终止线程的状态 任意时间点线程有且只有其中一种状态6种状态之间切换关系如下图所示。 四、Java线程安全
1. 线程安全程度 多线程访问同一对象不用考虑线程运行环境时的调度和交替执行也不使用同步手段或调用方不进行协调操作时使用该对象都能获取正确的结果则称该对象是线程安全的。根据安全层度分为5种依次降低不可变、绝对线程安全、相对线程安全、线程兼容、线程对立如下表所示。 线程安全程度 特点 不可变 1.不可变的共享对象一定是线程安全的无论是对象的方法还是调用者 2.共享数据是基本数据类型用final修饰来保证不可变 共享数据是对象数据类型需要对象自行保证自己的行为对其不受任何影响如String对象的substring()、replace()、concat()不会影响原值只返回一个新构造的字符串对象 3.不可变对象有final修饰的基本类型、String、AtomicInteger、AtomicLong。 绝对线程安全 1.“绝对安全”不管运行如何调用者都无需任何额外的同步手段 2.Java中绝大多数都不是绝对安全而是相对安全。 相对线程安全 1.“相对安全”对象单次操作是线程安全的调用时无需额外的同步手段 2.若是连续调用则需要调用端额外的同步手段 3.相对线程安全类Vector、HashTable等。 线程兼容 1.“线程兼容”对象本身不是线程安全需要调用端额外的同步手段 2.线程兼容的类ArrayList、HashMap等。 线程对立 1.“线程对立”无论是否同步都无法在多线程环境并发使用 2.Java天生支持多线程特性应避免尽可能避免线程对立会出现死锁。 注意线程安全是以多线程之间存在共享数据为前提不可变对象如String对象的substring()、replace()、concat()不影响原值只返回一个新构造的字符串对象。 现实线程安全有3种方式互斥同步阻塞同步、非阻塞同步、无同步如下表所示。 线程安全现实 特点 互斥同步 阻塞同步 1.“同步”多线程并发访问时保证共享数据同一时刻只能被一个线程使用 2.互斥是实现同步的手段如互斥量、信号量、临界区等手段 3.sychronized重量级实现互斥monitorenter、monitorexit两指令完成 a.当前线程持有锁后锁计数器1而monitorexit则锁计数器减一直到计数器为0时才释放锁 b.可重入即同一线程反复进入同步块不会出现自锁现象 c.当前持有锁线程没有释放锁之前其他线程无条件的被阻塞 4.Lock接口轻量级 a.必须在finally块中手动释放锁 b.获取锁时可以超时中断 c.可以实现公平锁、非公平锁 d.锁绑定多个条件 5.互斥同步属于悲观的并发策略 6.缺点线程阻塞和唤醒带来的性能开销。 非阻塞同步 1.基于冲突检查的乐观并发策略共享数据检查到冲突进行补偿措施如重试达到一致不需要其他线程挂起 2.常用处理器指令集比较并交换CAS_常用、交换Swap等 3.CASx86指令集使用cmpxchg完成CAS“ABA问题”解决时间戳控制版本。 无同步 1.线程安全的代码无需同步如可重入代码、ThreadLocal 2.“可重入代码”代码执行的任何时候中断去执行另一段代码而控制权返回时原程序不会出现任何错误和对结果的影响 3.所有可重入代码是线程安全的但是线性安全的代码不一定是可重入代码。 注意sychronized可重入即同一线程反复进入同步块不会出现自锁现象当前持有锁线程没有释放锁之前其他线程无条件的被阻塞JDK5后类库使用CAS操作Unsafe类完成但是用户无法使用CASJDK9后VarHandle类开放面向程序使用CAS。
2. 锁优化 JDK6各种锁优化技术自旋锁、自适应自旋锁、锁消除、锁粗化、轻量级锁、偏向锁如下表所示。 锁优化 特点 自旋锁 1.“自旋锁”等待获取锁的阻塞线程执行忙循环自旋而不是切换线程 2.自旋等待避免线程切换的开销但是占用CPU处理时间因此自旋超出限定次数仍没有成功则线程挂起 3.开启自旋-XX:UseSpining(JDK6默认开启)自旋次数-XX:PreBlockSpin10次。 自适应自旋锁 1.“自适应”自旋次数不固定由前一次在同一锁对象的自旋时间及锁状态决定 2.自旋等待成功获取锁且持有锁线程正在运行那么自旋可以多等待相对更长时间若是自旋很少能成功获得锁则以后获取这个锁时可能直接省掉自旋过程。 锁消除 1.“锁消除”即时编译在运行时一些同步代码被检测到不存在共享数据竞争的锁则进行锁消除 2.锁消除判定依据是逃逸分析数据支持堆数据不会被其他线程访问。 锁粗化 1.“锁粗化”连续操作都对同一对象加锁则把加锁同步的范围扩展粗化到整个操作的外部 2.适用连续StringBuffer::append()、循环体中加锁。 轻量级锁 1.“轻量级锁”两线程竞争同一把锁两条以上线程竞争同一把锁则轻量级锁非阻塞同步 _ CAS膨胀为重量级锁互斥同步; 2.轻量级加锁工作过程 step1程序进入同步代码块时判定对象是否被锁定锁标志位01状态 step2没有被锁定则在当前栈帧中创建锁记录Lock Record空间用于存储锁对象目前的“Mark Word”的拷贝 step3CAS操作把对象的“Mark Word”更新为锁记录Lock Record空间地址 step4更新成功则当前线程加锁成功且锁标志位改为00状态 更新失败说明至少存在另一线程产生相互竞争首先检查“Mark Word”是否指向当前线程的栈帧若是则说明当前已经持有锁直接进入代码块否则被其他线程线程已加锁。 step5存在两条以上线程竞争加锁则轻量级锁膨胀为重量级锁锁标志位10状态后续线程进入阻塞状态。 偏向锁 1.“偏向锁”锁无竞争的情况下把整个同步消除掉即持有偏向锁的线程无需同步操作。 2.进入偏向锁模式锁标志位01状态 偏向模式设置为1一旦有其他线程尝试获取锁则偏向模式结束、锁定对象是否处于锁定状态决定是否撤销偏向锁偏向模式设置为0、标志位转为01未锁定或00轻量级锁状态 3.JDK6启用偏向锁-XX:UseBiasedLocking。 偏向锁、轻量级锁的状态转换及对象Mark Word的关系如下图所示。 五、参考资料
Java线程一 _ 介绍_爱我所爱0505的博客-CSDN博客
volatile与synchronized实现原理_synchronized底层是总线锁吗_爱我所爱0505的博客-CSDN博客
Java内存模型一 _ 基础_爱我所爱0505的博客-CSDN博客
Java内存模型二 _ volatile/synchronized/final内存语义_volitale final sync_爱我所爱0505的博客-CSDN博客
Java线程三 _ 线程间通信_java 三个线程间通信_爱我所爱0505的博客-CSDN博客
Lock锁一 _ 基础_在lock锁的队列中,什么时候前一个节点会唤醒后一个节点_爱我所爱0505的博客-CSDN博客
Lock锁二 _ 重入锁/读写锁_读写锁可重入锁_爱我所爱0505的博客-CSDN博客
深入理解Java内存模型一——基础_Java_程晓明_InfoQ精选文章