Java虚拟机(十)内存模型与线程

JAVA内存模型
Java的内存模型的主要目标是定义程序中各个变量的访问规则,即在虚拟机中将变量存储到内存和从内存中取出变量这样的底层细节。这里说的变量包含了实例字段,静态字段和构成数据对象的元素(共享的),而不包括局部变量和方法参数,因为他们是线程私有的。

Java内存模型规定了所有变量都存储在主内存中,每个线程还有自己的工作内存,,工作内存中保存了被该线程使用到的变量的主内存副本拷贝,线程对变量的操作都必须在工作内存中进行。各个工作内存是互相隔离的,线程之间的变量值传递是通过主内存来完成。
在这里插入图片描述
对于主内存和工作内存之间具体的交互协议,java内存模型定义了8中操作来完成,每一项操作都是原子性的。
volatile关键字是Java虚拟机提供的最轻量级的同步机制。当一个变量被定义为volatile之后,他讲具备两种特性,一种是保证此变量对所有线程的可见性(一条线程修改了这个变量的值,其他线程可以立即得知),普通变量是做不到这点的,因为普通变量的值传递需要通过主内存来完成。
volatile变量在各个线程中是一致的,但基于volatile的运算在并发下并不是安全的。因为他只能保证可见性,并不能保证原子性。

下面这类场景就很适合使用volatile做控制鬓发,当shutdown()方法被调用时,能保证线程中执行的doWork()方法立刻停下来。
在这里插入图片描述
volatile第二个语意是禁止指令重排优化。普通变量只能保证所有依赖赋值结果的地方都能获取到正确的结果,而不能保证执行顺序。volatile可以。

原子性,可见性(volatile的保证可见性的规则是使新值立即同步到主内存,每次使用时从主内存刷新,synchronized和final也可以保证)与有序性(有序性是指在计算机内部指令的执行顺序,volatile和synchronized关键字能够保证有序性)。

Java实现线程有三种方式,使用内核线程实现,使用用户线程实现和用户线程加轻量级进程混合实现。
用户线程的建立,同步,销毁和调度完全在用户态中完成,不需要内核的帮助。如果程序实现得当,这种线程不需要切换到内核态,因此这种操作是非常快速且低消耗的。
不需要系统内核的支持意味着,“阻塞该如何处理”,“多处理器系统中如何将线程映射到其他处理器上”这样的问题需要自己负责。
对于Sun JDK来说,他的windows版与Linux版都是使用一对一的线程模型实现的,一条Java线程就映射到一条轻量级的进程之中。

Java线程调度,线程调度是指系统为线程分配处理器使用权的过程,分为协同式调度和抢占式调度。Java使用的线程调度方式是抢占式调度。Java可以通过设置优先级来建议系统给线程多分配一点执行时间。当线程出于Ready状态时,优先级越高越容易被系统选择执行。

Java语言定义了5中线程状态,在任意一个时间点,一个线程有且仅有其中一种状态。
新建(New):创建后尚未启动的线程出于这种状态。
运行(Runnable):Runnable包括了操作系统线程状态中的Running和Ready,就是线程有可能正在执行,也有可能正等待着CPU为他分配时间片。
无限期等待(Waiting):出于这种状态的线程不会被分配CPU执行时间,他们要等待其他线程显式地唤醒。
有限期等待(Timed Waiting):出于这种状态的线程不会被分配CPU执行时间,一定时间后会被自动唤醒。
阻塞(Blocked):阻塞状态出于等待获取锁的状态。
结束(Terminated):已终止线程的线程状态,线程已经结束执行。

线程安全的实现方法
互斥同步是常见的一种并发正确性保障手段,同步是指在多个线程并发访问共享数据时,保证共享数据在同一时刻只能被一个线程使用,互斥是实现同步的一种手段(临界区,互斥量,信号量)。
Java中最进本的互斥手段就是synchronized关键字。除了synchronized之外,我们还可以使用java.util.concurrent包中的重入锁来实现同步。重入锁增加了一些高级功能,等待可中断,可实现公平说,以及锁可以绑定多个条件。
等待可中断是指正在等待的线程可以选择放弃等待,改为处理其他事情。公平锁是指按照申请锁的时间顺序来依次获得锁。锁绑定多个条件是指一个重入锁可以同时绑定多个条件对象。

非阻塞同步
因为互斥同步会带来线程阻塞和唤醒所带来的性能问题,所以也被成为阻塞同步。互斥同步属于一种悲观锁的并发策略,总是认为会有人竞争。
我们还有另一种选择,基于冲突检测的乐观并发策略。先进行操作,如果没有其他线程争用共享数据,那操作就成功了。如果有争用,不断重试,直到成功为止。
乐观锁常见的有CAS,CAS需要三个操作数,分别是内存位置,旧的预期值,新值。当且仅当内存中的值符合旧值时采用新值替换他,否则就不更新。也有漏洞,ABA问题。

锁优化
自旋锁与自适应自旋。如果线程获取不到锁的时候需要挂起和恢复对性能会带来很大的压力。我们可以让后面请求锁的那个线程“稍等一下”,可以让线程执行一个忙循环(自旋)。
后面JDK中还引入了自适应自旋,意味着自旋等待的时间不在固定,而是由前一次在同一个锁上的自旋时间以及锁的拥有者的状态来决定。
锁消除
锁消除是指虚拟机在编译器启动时对不可能存在共享数据竞争的锁进行消除。
锁粗化
原则上,在编写代码时总是推荐奖同步块的范围尽可能缩小。但是如果一些列操作都是对同一个对象反复加锁和解锁,即使没有竞争,对于性能还是会有很大的损耗。这时会将锁的范围扩大。

轻量级锁
轻量级是相对于使用操作系统互斥量来实现的传统锁而言的,传统的锁机制称为“重量级”锁。轻量级锁并不是用来代替重量级锁的,本意是在没有多线程竞争的前提下,减少传统的重量级锁使用操作系统互斥量产生的消耗。
在HotSpot虚拟机的对象头Mark Word里会有当前是轻量级和重量级锁的标识。
虚拟机将使用CAS操作尝试将对象的Mark Word更新为指向Lock Record的指针,如果成功了,就拥有这个对象的锁。并且标记这个对象为轻量级锁的状态。
如果这个更新失败了,虚拟机将会检查对象的Mark word是否指向当前的栈帧,如果有两条以上的线程争用同一个锁,那轻量级锁就不在有效,要膨胀为重量级锁。

偏向锁
它的意思是这个锁会偏向于第一个获得它的线程,如果在接下来的执行过程中,该锁没有被其他线程获取,则持有该偏向锁的线程永远不需要再进行同步。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值