小目标之读懂JVM—线程安全与锁优化

17 篇文章 0 订阅
13 篇文章 0 订阅

上一章讲了java的内存模型以及虚拟机如何实现并发的问题。这一章还是对于并发问题的讲解,也是深入理解java虚拟机的最后一节。疫情管控更为严格了,有时候整天呆在家里呆久了感觉浑身不舒适,所以下一本书打算读解硬派健身-一平米健身的书籍。

这一章讲述的是java中的线程安全的问题。线程安全的问题针对的是涉及多个线程的共享变量,Java语言中操作共享的变量按照线程安全的安全程度可以分为5类:不可变、绝对线程安全、相对线程安全、线程兼容和线程对立。

不可变指的是对象被构建之后就不会发生改变,例如final变量,一旦声明初始化后便不允许改变,因此各个线程观察到它的状态都是一致的。

绝对线程安全指的是无论何种情况下,无需任何同步操作都不会引起数据结果不一致。这种情况基本不存在,即使java中声明为线程安全的类,比如vector和concurrentHashMap之类,在多线程条件下获取其size也会出现不一致的结果,一个线程遍历取,一个线程遍历移除元素的时候就可能导致移除之后size变化,取元素的线程因此返回数组越界异常。

相对线程安全就是通常意义上的线程安全,即对对象的单独操作是线程安全的,但是特定顺序的连续调用就需要加入额外的同步手段。如上面说的获取集合size遍历的情况。

线程兼容是指本身不是线程安全的,但是可以通过调用时的同步手段使其达到线程安全的情况。通常意义下所说的线程不安全指的就是这种情况。

线程对立是指无论是否采取同步措施,都无法达到并发情况下安全实用的效果,比如Thread类的suspend()和resume()方法。

线程安全的虚拟机实现方案有以下几种:

  1. 互斥同步 即共享数据每次只能被一个线程使用,剩下的想使用的线程排队等待,临界区(CriticalSection)、互斥量(Mutex)和信号量(Semaphore)都是主要的互斥实现方式。Java中通过synchronized即可实现互斥访问。

  2. 非阻塞同步 互斥同步主要问题在于需进行线程阻塞和唤醒,性能消耗较多,互斥同步也被叫做阻塞同步。互斥同步属于一种悲观锁,总是认为只要不做同步处理,共享数据肯定会出现问题,无论共享数据是否会真的出现竞争。随着硬件指令集发展,也可以采用基于冲突检测的乐观并发策略,即先进行操作,如果没有其它线程竞争,则操作成功,否则采取其它补偿措施(如不断重试,直到成功)。这种策略通常不需要挂起线程,所以这种同步也称为非阻塞同步。非阻塞同步需要依靠硬件指令去保证操作和检测冲突的原子性,硬件指令的比较并交换(Compare-and-Swap,CAS)达成了上述要求。Java的AtomicInteger类似的原子数据类中就运用了CAS。CAS进行更新数据时会判断如果当前值和要更新的旧值一致,则更新新值,否则不执行更新,这种情况存在一种ABA问题,即当前值A虽然可能和要更新的值A一致,但有可能它被别的线程更新过了两次,第一次更新为B,第二次再更新为A。虽然存在ABA问题,但大多数情况下ABA并不影响程序并发的正确性,如需解决ABA问题,采用之前互斥同步即可。

  3. 无同步方案 有些代码天然就是线程安全的,比如可重入代码(Reentrant Code),也叫纯代码,它不依赖一些公用系统资源和堆上数据,用到的变量均由参数传入,任何时候返回结果都可预测,只要输入了相同的数据,就能返回相同结果。再比如线程本地存储(Thread Local Storage),将共享数据的可见性限制在同一个线程之内,即可做到无须同步也能保证线程之间不出现数据争用的情况。这种特点的应用包括经典Web交互模型中的“一个请求对应一个服务器线程”之类。Java中可以通过java.lang.ThreadLocal类来实现线程本地存储的功能,比如日期格式化类SimpleDateFormat就可通过这种方式解决线程不安全的问题。

锁优化指虚拟机层面做出的一些优化锁性能和效率和技术,如适应性自旋、锁消除、锁粗化、轻量级锁、偏向锁等。

互斥时消耗最大的部分在于线程的挂起和恢复,如果共享数据只会锁定很短的时间,线程很短时间内去完成阻塞过程并不划算,便可以让后面请求锁的线程做一个自旋循环(即while(true){}),稍等一会,自适应自旋是可以根据同一个锁的历史自旋情况来去优化自旋策略。

锁消除是指一些共享数据虽然在堆上,但是一段时间内只会被一个线程用到,便可以消除锁。

锁粗化是指一系列操作都对一个操作反复加锁和解锁,那么可以将加锁和解锁扩展至整个加锁、解锁外部。

轻量级锁通过锁标志、锁记录和数据指针头的修改以及CAS操作,达到节省互斥开销的目的,它是基于“对于绝大部分锁,在同步周期内不存在竞争”的经验来达到优化同步性能的目的,在遇到锁竞争时,它会膨胀为重量级锁,也即互斥锁,所以锁竞争激烈时,它比完全采用互斥锁消耗更大。

偏向锁也是基于数据无竞争情况下的优化,它甚至不用CAS操作。它被第一个线程获得后,如果接下来未被其它线程获取,则第一个线程永远不需要进行同步。它也是可以提高带有同步但无竞争的程序性能,但在大多数锁总被多个不同线程访问时,也并不能优化性能。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值