深入理解JAVA虚拟机学习笔记(十三)

第13章 线程安全与锁优化

并发就完事了

13.1 概述

并发首先就是要保证正确性:避免数据在被中断期间修改和变脏。

13.2线程安全

当多个线程同时访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那就称这个对象是线程安全的

13.2.1 Java语言中的线程安全

不可变、绝对线程安全、相对线程安全、线程兼容和线程对立
1.不可变对象一定是线程安全的。
当多线程贡献的数据是一个基本数据类型:用final关键字修饰即可。
共享的是一个对象,可以采用java.lang.String的设计思想,当用户调用它的方法时不会影响它原来的值,而是返回一个新构造的字符串对象
2.绝对线程安全
即使像(Vector)这样全部被定义为同步的县城也不一定是安全的。
需要添加额外的手段去确保即使看上去很安全的线程,如(Vector)
3.相对线程安全
它需要保证对这个对象单次的操作是线程安全的,我们在调用的时候不需要进行额外的保障措施,但是对于一些特定顺序的连续调用,就可能需要在调用端使用额外的同步手段来保证调用的正确性。
4.线程兼容
线程兼容是指对象本身并不是线程安全的,但是可以通过在调用端正确地使用同步手段来保证对象在并发环境中可以安全地使用。
** 5.线程对立**
线程对立是指不管调用端是否采取了同步措施,都无法在多线程环境中并发使用代码。

13.2.2 线程安全的实现方法

如何实现线程安全与代码编写有很大的关系,但虚拟机提供的同步和锁机制也起到了至关重要的作用。
1.互斥同步
同步是指在多个线程并发访问共享数据时,保证共享数据在同一个时刻只被一条(或者是一些,当使用信号量的时候)线程使用。而互斥是实现同步的一种手段,临界区(Critical Section)、互斥量(Mutex)和信号量(Semaphore)都是常见的互斥实现方式。因此在“互斥同步”这四个字里面,互斥是因,同步是果;互斥是方法,同步是目的。
synchronized是一种块同步结构,前后分别有两个字节码指令:monitorenter和monitorexit。
在执行monitorenter指令时,首先要去尝试获取对象的锁。如果
这个对象没被锁定,或者当前线程已经持有了那个对象的锁,就把锁的计数器的值增加一,而在执行monitorexit指令时会将锁计数器的值减一。一旦计数器的值为零,锁随即就被释放了。如果获取对象锁失败,那当前线程就应当被阻塞等待,直到请求锁定的对象被持有它的线程释放为止。

同步块可重入,并且无法被强制释放,也无法被强制中断等待或退出。

Lock接口实现:
重入锁:增加了等待可中断、可实现公平锁及锁可以绑定多个条件。
推荐使用syc:
**Lock应该确保在finally块中释放锁,否则一旦受同步保护的代码块中抛出异常,则有可能永远不会释放持有的锁。**这一点必须由程序员自己来保证,而使用synchronized的话则可以由Java虚拟机来确保即使出现异常,锁也能被自动释放。
2.非阻塞同步
互斥同步面临的主要问题是进行线程阻塞和唤醒所带来的性能开销,因此这种同步也被称为阻塞同步(Blocking Synchronization)。
非阻塞同步也被称为乐观锁,即不管风险,先进行操作,如果没有其他线程的争用,即操作成功,如果冲突,就补救:(通常是重试)
java中CAS操作,大部分情况没有问题,但由于它独特的比较机制,在面对“ABA”问题时,会出现问题。
3.无同步方案
一些代码天然安全:因为它不依赖全局变量,存在对上的数据,以及公用的系统资源。

13.3锁优化

适应性自旋(Adaptive Spinning)、锁消除(Lock Elimination)、锁膨胀(Lock Coarsening)、轻量级锁(Lightweight Locking)、偏向锁(Biased Locking)

13.3.1 自旋锁与自适应自旋

大部分的线程执行时并不需要占用过多的执行时间,无需停止,只要让线程执行一个忙循环即可(自旋)。

发布了10 篇原创文章 · 获赞 2 · 访问量 269

猜你喜欢

转载自blog.csdn.net/qq_37492314/article/details/105429899