当前位置:首页 / 文章测试 / 线程安全与锁优化

线程安全与锁优化

开始打字练习

第13章 线程安全与锁优化

1. 线程安全

当多个线程同时访问一个对象时,如果不考虑这些线程的调度和执行,也不需要其他额外的操作,得到的结果都是我们期望的结果,则可以认为这个对象是线程安全的。

哪些对象是线程安全的?

1.1 不可变量 Immutable

被final声明的对象只要在构造的时候没有出现this逃逸这个对象就是线程安全的

a. 对于基本数据类型,定义时声明为 final即可

b. 对于对象数据类型,需要自己保证;如:String、Integer等对象

1.2 局部变量

由于局部变量不可能被多线程访问,所以肯定是线程安全的

2. 如何实现线程安全

2.1 互斥同步

a. synchronized

b. ReentrantLock: 与synchronized 相比,增加了一些高级的功能

1)等待可中断 2)实现了公平锁3)锁可以绑定多个条件

应该如何选择呢?

推荐优先使用synchronized 关键字

原因主要有以下几个:

1)synchronized 语法简单 2)LOCK需要释放锁,需要在finally中释放,synchronized锁的释放是由jvm 来保证的 3)经过十几年的优化,synchronized 的性能差距和LOCK已经变得很小,在绝大部分情况下都在一个可以接受的范围内

2.2 非阻塞同步

借助于硬件指令

1)测试并设置 Test-and-Set

2)获取并增加 Fetch-and-Increase

3)交换 Swap

4)比较并交换 Compare-and-Swap(CAS会有ABA问题产生)

5)加载链接/条件存储 Load-link/Store-Conditional

JDK5 之后在sun.misc.Unsafe 中提供了这些方法,这些方法会被编译成平台相关的指令

3. 锁优化

JDK5 到 JDK6 花费了大量的资源去实现各种锁优化技术,如适应性自旋(Adaptive Spinning)、锁消除(Lock Elimination)、锁膨胀(Lock Coarsening)、轻量级锁(Lightweight Locking)、偏向锁(Biased Locking) 等

锁的状态总共有四种:无锁状态、偏向锁、轻量级锁和重量级锁,随着锁的竞争,可以从偏向锁升级到轻量级锁,再升级到重量级锁(但是锁升级是单向的,也就是说只能从低到高升级,不会出现锁的降级)

轻量锁加锁过程如下:

1)检查同步对象当前有没有锁,如果锁状态为 01 (表示没有),在当前的线程中建立一个名为 Lock Record 的空间,把同步对象的 Mark Word 拷贝过来

2)虚拟机将使用CAS操作尝试将对象的Mark Word更新为指向Lock Record的指针,并将Lock Record里的owner指针指向 Object Mark word,如果成功则执行步骤(3),否则执行步骤(4)。

3)如果这个动作执行成功了,那么这个线程就拥有了该对象的锁,并且对象Mark Word的锁标志位设置为”00“,表示对象处于轻量锁的状态;

4)如果这个操作失败了,虚拟机首先会检查对象的Mark Word是否指向当前线程的栈帧,如果是就说明当前线程已经拥有了这个对象的锁,那就可以直接进入同步块继续执行。否则就说明这个对象已经被其他线程占用了,如果出现两个以上的线程争用同一个锁的情况,轻量级锁就会膨胀为重量级锁,锁标志的状态值变为”10“, Mark Word 中存储的就是指向重量级锁(互斥量)的指针,后面等待锁的线程也要进入阻塞状态。

3.2 偏向锁

偏向锁中的偏,指的是偏心的意思,它会偏向于第一个获得它的线程

锁对象第一次被线程获取时,虚拟机会将对象头中的标志位设置为01 把偏向模式设置为 1 ——表示进入偏向状态,同时把获取这个锁的线程的ID记录在Mark Word中

如果在接下来的执行过程中,持有偏向锁的线程以后每次进入到这个锁的同步块时,虚拟机都可以不再进行任何同步操作。

一旦出现另一个线程去尝试获取这个锁的情况,偏向模式马上宣告结束。

声明:以上文章均为用户自行发布,仅供打字交流使用,不代表本站观点,本站不承担任何法律责任,特此声明!如果有侵犯到您的权利,请及时联系我们删除。