自旋

volatile关键字的作用

巧了我就是萌 提交于 2020-01-20 17:45:29
引言   昨日接了一个阿里外包的电话面试,问了一些技术问题感觉到自己是真的菜,接触Java开发已经也有一段时间,技术方面说来惭愧,一直以来只是局限于框架工具的用法,也没有进行了解其实现的原理,更重要的是一直没有归纳和总结,这次把这些问题记录下来,相关的知识点也找了一些资料学习下。 问题 1. CountDownLanch的工作原理 实现原理:计数器的值由构造函数传入,并用它初始化AQS的state值。当线程调用await方法时会检查state的值是否为0,如果是就直接返回(即不会阻塞);如果不是,将表示该节点的线程入列,然后将自身阻塞。当其它线程调用countDown方法会将计数器减1,然后判断计数器的值是否为0,当它为0时,会唤醒队列中的第一个节点,由于CountDownLatch使用了AQS的共享模式,所以第一个节点被唤醒后又会唤醒第二个节点,以此类推,使得所有因await方法阻塞的线程都能被唤醒而继续执行。   引用别人的博客里的一段话,详细请点击: Java并发包中CountDownLatch的工作原理、使用示例 题外话: 什么是 AQS(抽象的队列同步器) AbstractQueuedSynchronizer类如其名,抽象的队列式的同步器,AQS定义了一套多线程访问 共享资源的同步器框架,许多同步类实现都依赖于它,如常用的 ReentrantLock/Semaphore

6.synchronized

半腔热情 提交于 2020-01-17 06:08:15
文章目录 1. 锁的升级 1.1. 偏向锁 1.2. 轻量级锁 1.3. 重量级锁 2. 锁与可见性(内存刷新) Java 中的每一个对象都可以作为锁。具体表现为以下3种形式。 对于普通同步方法,锁是当前实例对象。 对于静态同步方法,锁是当前类的 Class 对象。 对于同步方法块,锁是 Synchonized 括号里配置的对象。 1. 锁的升级 1.1. 偏向锁 偏向 的意思是,偏向锁假定将来只有第一个申请锁的线程会使用锁(不会有任何线程再来申请锁),这是一中乐观锁策略 加锁 只需要在Mark Word中CAS记录owner(本质上也是更新,但初始值为空),如果记录成功,则偏向锁获取成功,记录锁状态为偏向锁,以后当前线程等于owner就可以零成本的直接获得锁. 锁膨胀 在退出同步块时不会释放锁。只有在第二个线程过来产生竞争的时候才会在 全局安全点 进行释放或者膨胀为轻量锁的操作。 锁撤销 偏向锁的撤销,需要等待全局安全点(在这个时间点上没有正在执行的字节码)。它会首先暂停拥有偏向锁的线程,然后检查持有偏向锁的线程是否活着,如果线程不处于活动状态,则将对象头设置成无锁状态;如果线程仍然活着,拥有偏向锁的栈会被执行,遍历偏向对象的锁记录,栈中的锁记录和对象头的 Mark Word 要么重新偏向于其他线程,要么恢复到无锁或者标记对象不适合作为偏向锁,最后唤醒暂停的线程. 批量再偏向

Operating Systems-tep Chapter4和Chapter5 读书笔记

筅森魡賤 提交于 2020-01-17 01:47:32
Chapter4 插曲:线程API pthread_create 线程创建 pthread_join 等待线程执行完成,可以接收等待线程执行完后的返回值 一个创建多个线程去并行执行特定任务并行程序会用join来确保所有的工作都完成,才退出或进入下一阶段工作。 phread_mutex_lock 是对一个锁变量上锁,pthread_mutex_unlock是对一个锁变量进行解锁。pthread_mutex_init 是初始化锁变量 phread_mutex_trylock 会在锁已经被持有时返回失败,phread_mutex_timedlock 会在得到了锁或者超时之后返回,因此,timelock函数在超时值为0时会退化成trylock函数。 pthread_cond_wait 会使调用线程休眠,等待其他线程唤醒它,wait函数会在线程休眠前释放这个锁。 书中列举了一些tips: Chapter5 锁 评估锁 评估锁的几个条件: 锁能够实现基本的任务,即提供互斥量。基本地,这个锁有效吗,能够阻止多线程进入同一个临界区吗? 公平。一旦锁空闲了,是否每个竞争该锁的线程都被公平对待?是否有竞争该锁的线程处于饥饿状态而一直得不到锁? 性能。一个是没有竞争的情况:当只有一个线程在运行、获取、释放锁,锁的开销有多少?另一个是多线程在单CPU上竞争同一个锁的情况,是否需要担心其性能?最后一点

linux多线程同步

大憨熊 提交于 2020-01-14 00:21:31
1. 互斥量 是线程同步的一种机制,用来保护多线程的共享资源。同一时刻,只允许一个线程对临界区进行访问。互斥量的工作流程:创建一个互斥量,把这个互斥量的加锁调用放在临界区的开始位置,解锁调用放到临界区的结束位置。当内核优先把某个线程调度到临界区的开始位置时,线程执行这个加锁调用,并进入临界区对资源进行操作。此时其他线程再被内核调度到这里的时候,由于该互斥量已被加锁状态,得不到锁会一直阻塞在这里,导致其他线程不能进入临界区,直到刚刚那个进入临界区的线程离开临界区并执行解锁调用。 2. 条件变量 与互斥量不同,互斥量是防止多线程同时访问共享的互斥变量来保护临界区。条件变量是多线程间可以通过它来告知其他线程某个状态发生了改变,让等待在这个条件变量的线程继续执行。通俗一点来讲:设置一个条件变量让线程1等待在一个临界区的前面,当其他线程给这个变量执行通知操作时,线程1才会被唤醒,继续向下执行。 3. 读写锁 与互斥量的功能类似,对临界区的共享资源进行保护!互斥量一次只让一个线程进入临界区,读写锁比它有更高的并行性。读写锁有以下特点: 如果一个线程用读锁锁定了临界区,那么其他线程也可以用读锁来进入临界区,这样就可以多个线程并行操作。但这个时候,如果再进行写锁加锁就会发生阻塞,写锁请求阻塞后,后面如果继续有读锁来请求,这些后来的读锁都会被阻塞!这样避免了读锁长期占用资源,防止写锁饥饿

JVM常用命令参数

半腔热情 提交于 2020-01-13 01:11:34
(1)-Xms20M 表示设置JVM启动内存的最小值为20M,必须以M为单位  m:memory (2)-Xmx20M 表示设置JVM启动内存的最大值为20M,必须以M为单位。将-Xmx和-Xms设置为一样可以避免JVM内存自动扩展。 (3)-verbose:gc 表示输出虚拟机中GC的详细情况 (4)-Xss128k 表示可以设置虚拟机栈的大小为128k  s:stack (5)-Xoss128k 表示设置本地方法栈的大小为128k。不过HotSpot并不区分虚拟机栈和本地方法栈(HotSpot此参数无效) (6)-XX:PermSize=10M 表示JVM初始分配的永久代的容量,必须以M为单位 (7)-XX:MaxPermSize=10M 表示JVM允许分配的永久代的最大容量,必须以M为单位,大部分情况下这个参数默认为64M (8)-Xnoclassgc 表示关闭JVM对类的垃圾回收 (9)-XX:+TraceClassLoading 表示查看类的加载信息 (10)-XX:+TraceClassUnLoading 表示查看类的卸载信息 (11)-XX:NewRatio=4 表示设置年轻代:老年代的大小比值为1:4,这意味着年轻代占整个堆的1/5 (12)-XX:SurvivorRatio=8 表示设置2个Survivor区:1个Eden区的大小比值为2:8,这个参数默认为8

java锁

妖精的绣舞 提交于 2020-01-11 18:25:17
java中锁的实现有两种:synchronized和ReentrantLock。 synchronized synchronized是java虚拟机的内置锁 对于同步块:编译之后,会在同步块的前后形成 monitorenter 和 monitorexit 两个字节码指令,每个对象都具有一个monitor与之关联,拥有了monitor的线程便锁住了对象。 对于同步方法:编译之后,该同步方法有一个同步标志来表明该方法是同步方法。 synchronized对同一个线程是可重入的,进去一次monitor,count就加1。 java的线程是映射到操作系统的原生系统之上的,如果要阻塞或唤醒一个线程,都需要操作系统来帮忙完成,这就需要从用户态转换到核心态中,状态转换需要耗费很多的时间。所以synchronized属于一个重量级锁。之后虚拟机也进行了优化,比如:自旋锁,自适应自旋锁,轻量级锁,偏向锁。 自旋锁:线程没有获取到锁便进入忙循环,直到获取到锁。 自适应自旋:因为锁占用的时间长的话,自旋的时间太长浪费处理器资源,所以有了自适应自旋,自适应自旋的自旋时间不再固定,而是根据前一次在同一个锁上的自旋时间决定的。如果对于某个锁自旋很少成功,则省掉自旋过程,避免浪费资源,如果刚刚获得过成功,则自旋等待的时间持续相对的更长。 轻量级锁:在不存在锁竞争的时候起作用,当有锁竞争时会膨胀为重量级锁

10. 锁优化

情到浓时终转凉″ 提交于 2020-01-11 07:31:25
1. 自旋锁与适应性自旋锁 若物理机器上有一个以上的处理器,能让两个或两个以上的线程同时执行,我们可以让后面的请求锁的那个线程“稍等一下”,但是不放弃处理器的执行时间,看看持有锁的线程是否会很快就释放锁。为了让线程等待,我们只需让线程执行忙循环(自旋),这项技术称为自旋锁。 自旋锁在 JDK1.4.2 引入,但默认是关闭的。在 JDK1.6 中默认开启。优点是,避免了互斥同步挂起和恢复线程带来的巨大消耗。缺点是,若锁被占用的时间很长,自旋的线程恢白白消耗处理器资源,会带来性能上的浪费。为解决此问题,规定自旋等待的时间是有一定限度的,默认为10次,超过这个限制就会挂起线程。 JDK1.6 中引入了自适应的自旋锁。它的自旋时间不再固定,是由前一次在同一个锁上的自旋时间及锁的拥有者决定的。 2. 锁消除 锁消除是指虚拟机即时编译器在运行时,对一些代码上同步要求,但是检测到不可能存在共享数据竞争的所进行消除。 3. 锁粗化 若虚拟机检测到有一连串的零碎操作都对同一个对象加锁,将会把加锁同步的范围扩展(粗化)到整个操作序列的外部。 4. 轻量级锁 它是在 JDK1.6 之中加入的。它的本意是在没有多线程竞争的前提下,较少传统的重量级锁使用系统互斥量产生的性能消耗。它的加锁和解锁都是通过CAS操作来完成的。在没有竞争的情况下,轻量级锁使用CAS操作来避免使用互斥量带来的开销,但若存在锁竞争

JVM中锁优化,偏向锁、自旋锁、锁消除、锁膨胀

雨燕双飞 提交于 2020-01-08 10:25:16
详见: http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt364 本文将简单介绍HotSpot虚拟机中用到的锁优化技术。 自旋锁 互斥同步对性能最大的影响是阻塞的实现,挂起线程和恢复线程的操作都需要转入内核态中完成,这些操作给系统的并发性能带来了很大的压力。而在很多应用上,共享数据的锁定状态只会持续很短的一段时间。若实体机上有多个处理器,能让两个以上的线程同时并行执行,我们就可以让后面请求锁的那个线程原地自旋(不放弃CPU时间),看看持有锁的线程是否很快就会释放锁。为了让线程等待,我们只须让线程执行一个忙循环(自旋),这项技术就是自旋锁。 如果锁长时间被占用,则浪费处理器资源,因此自旋等待的时间必须要有一定的限度,如果自旋超过了限定的次数仍然没有成功获得锁,就应当使用传统的方式去挂起线程了(默认10次)。 JDK1.6引入自适应的自旋锁:自旋时间不再固定,由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定。如果在同一个锁对象上,自旋等待刚刚成功获得过锁,并且持有锁的线程正在运行中,那么虚拟机就会认为这次自旋也很有可能再次成功,进而它将允许自旋等待持续相对更长的时间。 锁削除 锁削除是指虚拟机即时编译器在运行时,对一些代码上要求同步,但是被检测到不可能存在共享数据竞争的锁进行削除

Java高并发实战,锁的优化

烂漫一生 提交于 2020-01-08 07:01:13
锁优化 这里的锁优化主要是指 JVM 对 synchronized 的优化。 自旋锁 互斥同步进入阻塞状态的开销都很大,应该尽量避免。在许多应用中,共享数据的锁定状态只会持续很短的一段时间。自旋锁的思想是让一个线程在请求一个共享数据的锁时执行忙循环(自旋)一段时间,如果在这段时间内能获得锁,就可以避免进入阻塞状态。 自旋锁虽然能避免进入阻塞状态从而减少开销,但是它需要进行忙循环操作占用 CPU 时间,它只适用于共享数据的锁定状态很短的场景。 在 JDK 1.6 中引入了自适应的自旋锁。自适应意味着自旋的次数不再固定了,而是由前一次在同一个锁上的自旋次数及锁的拥有者的状态来决定。 锁消除 锁消除是指对于被检测出不可能存在竞争的共享数据的锁进行消除。 锁消除主要是通过逃逸分析来支持,如果堆上的共享数据不可能逃逸出去被其它线程访问到,那么就可以把它们当成私有数据对待,也就可以将它们的锁进行消除。 对于一些看起来没有加锁的代码,其实隐式的加了很多锁。例如下面的字符串拼接代码就隐式加了锁: public static String concatString(String s1, String s2, String s3) { return s1 + s2 + s3; } String 是一个不可变的类,编译器会对 String 的拼接自动优化。在 JDK 1.5 之前,会转化为

Java并发系列-AQS源码学习

自闭症网瘾萝莉.ら 提交于 2020-01-07 22:09:29
目录 AQS框架学习 Node节点 等待队列 独占模式 共享模式 小结: AQS框架学习 Node节点 状态表示 cancelled:表明当前线程已经放弃锁 signal:表明当前线程正在运行,它后面的线程等着被它唤醒 condition:表明当前线程正在有条件的等待 propagate:表示下一次共享状态获取将会传递给后继结点获取这个共享同步状态。 两种模式 SHARED ,共享模式,一个空的Node对象 EXCLUSIVE,独占模式,直接是null 等待队列 AQS提供一个等待队列,该队列是一个双向链表结构,用来接收为获取到锁的线程所生成的node,该队列的head和tail都是同一个空的Node实例,并且都是懒加载。该队列所有的操作都是基于CAS的,所以可以保证线程安全性,每次添加新的node节点都是添加在链表的尾部。 独占模式 获取锁 如果获取锁失败且将当前线程添加到等待队列失败,则中断当前线程,代码如下: 当然,尝试获取锁在AQS框架里是一个抽象方法,让其子类来实现。独占模式下,Node节点的nextWaiter为null,在将当前线程添加到队列时,需要检查队列是不是还没有初始化。如果队列是有效的,则将当前线程所生成的节点添加到队列的尾部。当当前线程所生成的node节点处于排队期的时候,当前线程就开始以独占形式进行自旋去不断地尝试获取锁,因为尝试获取锁是在排队之前执行的