自旋

synchronized底层实现原理

不羁的心 提交于 2019-12-01 18:39:08
基于进入和退出管程(Monitor)对象实现,无论显式(Monitorenter Monitorexit)还是隐式都是如此。同步方法并不是由monitorenter和monitorexit指令来实现同步的,而是由方法调用指令读取运行时常量池中的ACC_SYNCHRONIZED标志来隐式实现的。 在JVM中,对象在内存中的布局分为三块区域:对象头、实例数据和对齐填充。 实例变量:存放类的属性数据信息,包括父类的属性信息,如果是数组的实例部分还包括数组的长度,这部分内存按4字节对齐。 填充数据:由于虚拟机要求对象起始地址必须是8字节的整数倍。填充数据不是必须存在的,仅仅是为了字节对齐,这点了解即可 而对于顶部,则是Java头对象,它实现synchronized的锁对象的基础,这点我们重点分析它,一般而言,synchronized使用的锁对象是存储在Java对象头里的,jvm中采用2个字来存储对象头(如果对象是数组则会分配3个字,多出来的1个字记录的是数组长度),其主要结构是由Mark Word 和 Class Metadata Address 组成 虚拟机位数 头对象结构 说明 32/64bit Mark Word 存储对象的hashCode、锁信息或分代年龄或GC标志等信息 32/64bit Class Metadata Address 类型指针指向对象的类元数据

Java线程并发中常见的锁

大憨熊 提交于 2019-12-01 11:07:58
随着互联网的蓬勃发展,越来越多的互联网企业面临着用户量膨胀而带来的并发安全问题。本文着重介绍了在java并发中常见的几种锁机制。 1.偏向锁   偏向锁是JDK1.6提出来的一种锁优化的机制。其核心的思想是,如果程序没有竞争,则取消之前已经取得锁的线程同步操作。也就是说,若某一锁被线程获取后,便进入偏向模式,当线程再次请求这个锁时,就无需再进行相关的同步操作了,从而节约了操作时间,如果在此之间有其他的线程进行了锁请求,则锁退出偏向模式。在JVM中使用-XX:+UseBiasedLocking package jvmProject; import java.util.List; import java.util.Vector; public class Biased { public static List<Integer> numberList = new Vector<Integer> (); public static void main(String[] args) { long begin = System.currentTimeMillis(); int count = 0 ; int startnum = 0 ; while (count<10000000 ){ numberList.add(startnum); startnum +=2 ; count ++ ; }

Java并发优化思路

夙愿已清 提交于 2019-12-01 11:07:46
一、并发优化 1.1、Java高并发包所采用的几个机制 (1)、CAS(乐观操作) jdk5以前采用synchronized,对共享区域进行同步操作, synchronized是重的操作,在高并发情况下,会引起线程频繁切换 ;而 CAS是一种乐观锁机制,compare and swap,不加锁 ,而是假设没有冲突去完成,若有冲突会重试(非阻塞)。compare&swap是原子操作,基于CPU的原语操作。 CAS仍然存在 三大问题:ABA问题,循环时间长开销大,以及只能保证一个共享变量的原子操作 。 (2)、Volatile(变量的可见性) VM阻止volatile变量的值放入处理器的寄存器 ,在写入值以后会被从处理器的cache中flush掉,写到内存中去,这样其他线程都可以立刻看到该变量的变化。 (3)、AQS,抽象队列同步器(原子性操作状态同步位、有序队列、阻塞唤醒进程) 获取锁:首先判断当前状态是否允许获取锁,如果是就获取锁,否则就阻塞操作或者获取失败,也就是说如果是独占锁就可能阻塞,如果是共享锁就可能失败。另外如果是阻塞线程,那么线程就需要进入阻塞队列。当状态位允许获取锁时就修改状态,并且如果进了队列就从队列中移除。 释放锁:这个过程就是修改状态位,如果有线程因为状态位阻塞的话就唤醒队列中的一个或者更多线程。 1.2、锁优化 今天所说的锁优化,是指在阻塞式的情况下

Java并发:线程安全与锁优化

时间秒杀一切 提交于 2019-12-01 11:07:08
概述 人们很难想象现实中的对象在一项工作进行期间,会被不停地中断和切换,对象的属性(数据)可能会在中断期间被修改和变“脏”,而这些事情在计算机世界中则是很正常的事情。有时候,良好的设计原则不得不向现实做出一些让步,我们必须让程序在计算机中正确无误地运行,然后再考虑如何将代码组织得更好,让程序运行更快。对于“高效并发”来说,首先需要保证并发的正确性,然后在此基础上实现高效。 1.线程安全 《Java Concurrency In Practice》的作者Brian Goetz对“线程安全”有一个比较恰当的定义:“当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那这个对象是线程安全的”。 这个定义比较严谨,它要求线程安全的代码都必须具备一个特征:代码本身封装了所有必要的正确性保障手段(如互斥同步等),令调用者无须关心多线程的问题,更无须自己采取任何措施来保证多线程的正确调用。这点听起来简单,但其实并不容易做到,在大多数场景中,我们都会将这个定义弱化一些,如果把“调用这个对象的行为”限定为“单次调用”,这个定义的其他描述也能够成立的话,我们就可以称它是线程安全了。 1.1 Java语言中的线程安全 按照线程安全的“安全程度”由强至弱来排序

并发与竟态小计

白昼怎懂夜的黑 提交于 2019-12-01 06:52:08
并发与竟态,以及自旋锁,原子操作等 并发:多个任务并行执行。 竟态:并发的线程对共享资源的访问容易发生竟态。 竟态发生情况举例: 1.多处理器SMP的多个cpu,smp是一种紧耦合,共享总线存贮的系统模型。 2.单CPU内进程与抢占他的进程。 3.中断与进程之间:硬中断,软中断,tasklet,低半部。 竟态的解决方案:途径是保证对共享资源的互斥访问。访问共享资源的代码区域是临界区,临界区要i有互斥机制。 互斥机制有: 1.中断屏蔽 2.原子操作 3.自旋锁 4.信号量 死锁的因素: 1.多进程调度导致的死锁 1.1互相排斥1.2循环等待1.3部分分配1.4缺少优先权 2.单线程导致死锁 引用引起阻塞的函数,比如:copy_from_user();还有自旋锁进程又调用自旋锁函数。 几个概念: 互斥锁=互斥量,mutex,是信号量semaphore在取值0/1时的特例。 来源: https://www.cnblogs.com/zhihui-3669/p/11665002.html

锁优化

被刻印的时光 ゝ 提交于 2019-11-30 17:59:36
锁优化 为了在线程之间更高效地共享数据,解决竞争问题,从而提高程序的执行效率,实现了多种锁优化技术。 自旋锁与自适应自旋 互斥同步对性能最大的影响:阻塞的实现,挂起和恢复线程的操作都需要转入内核态中完成。由于在很多时候,共享数据的锁定状态只会持续很短的时间,为了这段时间去进行挂起和恢复线程并不值得。因此可以考虑:当物理机有多个处理器,可以多次并行执行多个线程,此时可以让后面请求锁的线程稍等一会儿,但不放弃处理器的执行时间,看看持有锁的线程是否很快释放锁。为了让线程等待,会让线程执行一个忙循环(自旋)。 自旋锁 :如果锁被占用的时间比较短,自旋等待的效果很好,避免了线程切换的开销。如果锁占用时间很长,自旋只会白白消耗处理器资源,因此自旋等待的时间会有一个限度。一般自旋次数 默认是 10次。自旋等待不能代替阻塞,主要原因是 虽然避免了线程切换的开销,但是需要占用处理器的执行时间。 自适应自旋 :自适应表示 自旋的时间不固定,而是由 前一次在同一个锁上的 自旋时间 及 锁的拥有者状态 来决定。 锁消除 对于一些代码要求同步,但被检测不可能存在共享数据竞争。此时会将同步锁移除。 锁粗化 原则上:总是要求 同步块的作用范围尽可能的小。这样是使得 需要同步的操作数量尽可能变小,如果存在锁竞争,等待锁的线程也能尽快的拿到锁。 如果虚拟机检测到有一连串的操作 都是对同一个对象加锁

线程

こ雲淡風輕ζ 提交于 2019-11-29 05:35:27
执行线程 // 第一种:实现Runnable new Thread(new Runnable() { @Override public void run() { // ... } }).start(); // 第二种:继承Thread new Thread() { @Override public void run() { // ... } }.start(); synchronized 如果是 static 方法,锁的是 class 对象,在字节码中无法看到它的锁,即 monitor 对象监视器。 monitorenter,monitorexit Java 对象 包含对象头、实例数据、对齐填充。 对象头包含:对象自身的运行数据,如:哈希码、GC 分代年龄、锁标志位、持有该对象锁的线程数(每次monitorenter时加一)、指向轻量级锁记录的指针、指向重量级锁的指针、GC 标记等。 重量级锁:非可重入的锁 CAS:通过 Linux 内核中的 cmpxchg 函数来实现。 synchronized:加锁(偏向锁-》轻量级锁-〉重量级锁)。把 monitor 加入到 JVM 维护的阻塞队列。ContentionList -》EntryList -〉。。。 wait():释放锁,并加入到阻塞队列 自旋锁:循环进行 cmpxchg。为减少线程上下文切换而引入的。 Lock:使用自旋锁

并发下常用关键字的原理

风流意气都作罢 提交于 2019-11-28 17:25:20
对Synchronized的理解 synchronized关键字解决的是多个线程之间访问资源的同步性,synchronized关键字可以保证被它修饰的方法或者代码块在任意时刻只能有一个线程执行。 另外,在 Java 早期版本中,synchronized属于重量级锁,效率低下,因为监视器锁(monitor)是依赖于底层的操作系统的 Mutex Lock 来实现的,Java 的线程是映射到操作系统的原生线程之上的。如果要挂起或者唤醒一个线程,都需要操作系统帮忙完成,而操作系统实现线程之间的切换时需要从用户态转换到内核态,这个状态之间的转换需要相对比较长的时间,时间成本相对较高,这也是为什么早期的 synchronized 效率低的原因。在 Java 6 之后 Java 官方对从 JVM 层面对synchronized 较大优化,所以现在的 synchronized 锁效率也优化得很不错了。JDK1.6对锁的实现引入了大量的优化,如自旋锁、适应性自旋锁、锁消除、锁粗化、偏向锁、轻量级锁等技术来减少锁操作的开销。 synchronized关键字最主要的三种使用方式: 修饰实例方法: 作用于当前对象实例加锁,进入同步代码前要获得当前对象实例的锁 修饰静态方法: :也就是给当前类加锁,会作用于类的所有对象实例,因为静态成员不属于任何一个实例对象,是类成员( static

JVM学习(九):锁优化

房东的猫 提交于 2019-11-28 12:34:22
1、高效并发是JDK1.5到JDK1.6的一个重要改进,出现了 适应性自旋 (Adaptive Spinning)、 锁消除 (Lock Elimination)、 锁粗化 (Lock Coarsening)、 轻量级锁 (Lightweight Locking)和 偏向锁 (Biased Locking)等一系列锁优化技术。 2、自旋锁与自适应自旋: (1) 自旋锁 :互斥同步对性能最大的影响是阻塞的实现需要转入内核态完成,由于通常情况下共享数据的锁定状态只会持续很短的一段时间,如果让一个线程在等待获取锁的时候多等一会, 占用CPU的执行时间 但是不会进入阻塞状态, 执行一个忙循环 (自旋),即实现了自旋锁。 (2)自旋锁在JDK1.4.2中已经引入, 自旋不能代替阻塞 ,因为自旋需要占用处理器时间,自旋等待的时间必须有限制,默认自旋次数为10次,可以使用参数 -XX:PreBlockSpin 设置。 (3) 自适应自旋 :JDK1.6引入了自适应的自旋锁,即自旋的时间不再固定,而是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定。如果判断出自旋获取锁的成功概率大,则允许自旋等待较长时间,反之可能省略自旋过程直接进入阻塞,避免浪费处理器资源。 3、 锁消除 :虚拟机即时编译器在运行时,对于有些代码上同步,但被检测到不可能存在共享数据竞争的锁进行消除, 无需同步直接执行

JDK1.6 对 synchronized 的锁优化

房东的猫 提交于 2019-11-28 12:17:02
1. 背景 在 JDK 1.6 中对锁的实现引入了大量的优化。 目的 减少锁操作的开销 。 2. 锁优化 在看下面的内容之间,希望大家对 Mark Word 有个大体的理解。Java 中一个对象在堆中的内存结构是这样的: Mark Word 是这样的: 2.1 适应性自旋锁 自旋锁的思想: 让一个线程在请求一个共享数据的锁时执行忙循环(自旋)一段时间,如果在这段时间内能获得锁,就可以 避免进入阻塞状态 。 自旋锁的缺点: 需要进行忙循环操作 占用 CPU 时间 ,它只适用于 共享数据的锁定状态很短的场景 。 若锁被其他线程长时间占用,会带来许多性能上的开销。所以自旋的次数不再固定。由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定。 如果共享数据的锁定状态持续时间较短,切换线程不值得(会有上下文切换),可以利用自旋锁尝试一定的次数。 2.2 锁消除 JIT 编译时,会去除不可能存在竞争的锁。通过 JIT 的逃逸分析 来 消除一些没有在当前同步块以外被其他线程共享的数据的锁的保护 ,通过逃逸分析在 TLAB 来分配对象,这样就不存在共享数据带来的线程安全问题。 2.3 锁粗化 减少不必要的紧连在一起的 lock,unlock 操作, 将多个连续的锁扩展成一个范围更大的锁 。 2.4 偏向锁(重入锁) 为了在无线程竞争的情况下避免在锁获取过程中执行不必要的 CAS 原子指令,因为