前言
线程安全是并发编程中最重要的关注点;造成线程安全问题主要有两点:一是存在共享数据即临界资源、二是存在多个线程共同操作共享数据。解决方案就是保证同一时刻有且仅有一个线程在操作共享数据,其他线程必须等待该线程处理完数据后再进行,即互斥锁。在Java中,可以使用synchronized关键字实现。
一、synchronized的三种使用方式
- 修饰方法:作用于当前对象实例,进入同步代码前要获取当前对象的锁。
- 修饰静态方法:作用于当前类对象,进入同步代码前要获取当前实例对象的锁。
- 修饰代码块:执行加锁对象,对指定的加锁对象加锁。进入同步代码块之前要获的给定对象的锁,但是synchronized代码块上是给Class类上锁。
二、底层原理
- synchronized同步语句块:Synchronized同步语句块的实现使用的是monitorenter和monitorexit指令,其中monitorenter指令指向同步代码开始的地方,monitorexit指令则是知名同步代码块结束的位置。
- synchronized修饰方法:synchronized修饰的方法并没有monitorenter和monitorexit指令,取而代之的是ACC——SUNCHRONIEZED标识,该标识知名了改方法是一个同步方法,JVM通过ACC_SYNCHRONIZED访问标志来辨别一个方法是否声明为同步方法,从而执行相应的同步调用
三、JDK1.6之后的底层优化
- [1] 偏向锁:偏向锁在无竞争的情况下会把整个同步都消除掉。
- [2] 轻量级锁:在没有多线程竞争的前提下,减少传统的重量级锁使用操作系统互斥量产生的性能消耗,因为使用轻量级锁时,不需要申请互斥量。另外,轻量级锁的加锁和解锁都用到了CAS操作。
- [3] 自旋和自适应自旋锁:一般线程持有锁的时间不会太长,所以仅仅为了这一点时间去挂起线程/恢复线程是得不偿失的,为了让一个线程等待,我们只需要让线程执行一个忙循环(自旋),这项技术叫做自旋。在1.6中引入了自适应的自旋锁。自适应的自旋锁带来的改进就是:自旋的时间不再固定了,而是和前一次同一个锁上的自旋时间以及锁的拥有者的状态来决定,虚拟机变得越来越“聪明”。
- [4] 锁消除和锁粗化
四、Synchronized和ReetrantLock的对比
- 两者都是可重入锁。
- synchronized依赖于JVM,ReentrantLock依赖于API。
- 相比于synchronized,ReentrantLock增加了一些高级功能。主要有三点:等待可中断,可实现公平锁、可实现选择性通知(锁可以绑定多个条件)。
线程安全是并发编程关键,Java中可用synchronized关键字解决。本文介绍了synchronized的三种使用方式,包括修饰方法、静态方法和代码块;阐述了其底层原理,以及JDK1.6之后的底层优化,如偏向锁、轻量级锁等;还对比了Synchronized和ReetrantLock。

204

被折叠的 条评论
为什么被折叠?



