相信很多同学对synchronized的使用上不陌生,之前也给大家讲解过它的使用。本篇主要带大家深入了解一下它,大家也可以自己试着总结一下,这也是面试中常常问到的,单纯的回答它的基本使用,是惊艳不到面试官的~。
创新互联建站-云计算及IDC服务提供商,涵盖公有云、IDC机房租用、成都多线机房、等保安全、私有云建设等企业级互联网基础服务,联系热线:13518219792
从字面意思翻译过来就是同步的意思,所以它也叫同步锁,我们通常会给某个方法或者某块代码加上Synchronized锁来解决多线程中并发带来的问题,它也是最常用,最简单的一种方法。
在Java中,锁基本上都是基于对象而言的,所以又称为对象锁, 一个类通常只有一个class对象和n个实例对象,它们共享class对象,而我们有时候会对class对象加锁,所以又称为class对象锁。
这里大家要注意的是对象需要是一个非null的对象,我们通常也叫做对象监视器(Object Monitor)。
在JDK 1.5之前,它是一个重量级锁,我们通常都会使用它来保证线程同步。在1.5的时候还提供了一个Lock接口来实现同步锁的功能,我们只需要显式的获取锁和释放锁。
在1.5的时候,Synchronized它依赖于操作系统底层的Mutex Lock实现,每次释放锁和获取锁都会导致用户态和内核态的切换,从而增加系统性能的开销,当出现大并发的情况下,锁竞争会比较激烈,性能显得非常糟糕,所以称为重量级锁,所以大家往往会选择Lock锁。
但是Synchronized又是那么的简单好用,又是官方自带的,怎么可能放弃呢?所以在1.6之后,引入了大量的锁优化,比如自旋锁,轻量级锁, 偏向锁等,下面我们逐个看一下。
我们了解锁优化之前,我们先看一下它的实现原理。
首先我们看下同步块中,因为它是关键字,我们看不到源码实现,所以只能反编译看一下,通过 javap -v **.class。
public static void main(String[] args) {
synchronized(Demo.class) {
System.out.println("hello");
}
}
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=3, args_size=1
0: ldc #2 // class com/thread/base/Demo
2: dup
3: astore_1
4: monitorenter
5: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
8: ldc #4 // String hello
10: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
13: aload_1
14: monitorexit
15: goto 23
18: astore_2
19: aload_1
20: monitorexit
21: aload_2
22: athrow
23: return
我们重点关注monitorenter和monitorexit,那么他俩是什么意思呢?
monitorenter,如果当前 monitor 的进入数为 0 时,线程就会进入 monitor,并且把进入数 + 1,那么该线程就是 monitor 的拥有者 (owner)。如果该线程已经是 monitor 的拥有者,又重新进入,就会把进入数再次 + 1。也就是可重入。
monitorexit,执行 monitorexit 的线程必须是 monitor 的拥有者,指令执行后,monitor 的进入数减 1,如果减 1 后进入数为 0,则该线程会退出 monitor。其他被阻塞的线程就可以尝试去获取 monitor 的所有权。指令出现了两次,第 1 次为同步正常退出释放锁;第2次为发生异步退出释放锁。
我们再来看一下, 修饰实例方法中的表现:
class Demo {
public synchronized void hello() {
System.out.println("hello");
}
}
public synchronized void hello();
descriptor: ()V
flags: ACC_PUBLIC, ACC_SYNCHRONIZED
Code:
stack=2, locals=1, args_size=1
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String hello
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 25: 0
line 26: 8
LocalVariableTable:
Start Length Slot Name Signature
0 9 0 this Lcom/thread/base/Demo;
}
我们重点关注ACC_SYNCHRONIZED,它作用就是一旦执行到这个方法时,就会先判断是否有标志位,如果有,就会先尝试获取 monitor,获取成功才能执行方法,方法执行完成后再释放 monitor。在方法执行期间,其他线程都无法获取同一个 monitor。归根结底还是对 monitor 对象的争夺,只是同步方法是一种隐式的方式来实现。
synchronized 在 JVM 里的实现就是基于进入和退出 monitor 来实现的,底层则是通过成对的 MonitorEnter 和 MonitorExit 指令来实现。
有了以上的认识,下面我们就看看锁优化。
自旋锁,之前我们讲FutureTask源码的时候,有一个内部方法awaitDone(),给大家有介绍过,就是基于它实现的,今天再给大家总结一下。
它的目的是为了避免阻塞和唤醒的切换,在没有获得锁的时候就不进入阻塞,不断地循环检测锁是否被释放。但是,它也有弊端,我们通常来讲,一个线程占用锁的时间相对较短,但是万一占用很长时间怎么办?这样会占用大量cpu时间,这样会导致性能变差,所以在1.6引入了自适应自旋锁来满足这样的场景。
那么什么是自适应自旋锁呢?自旋的次数不是固定的,而是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定。如果此次自旋成功了,很有可能下一次也能成功,于是允许自旋的次数就会更多,反过来说,如果很少有线程能够自旋成功,很有可能下一次也是失败,则自旋次数就更少。这样一来,就能够更好的利用系统资源。
锁消除是一种锁的优化策略,这种优化更加彻底,在 JVM 编译时,通过对运行上下文的扫描,去除不可能存在共享资源竞争的锁。这种优化策略可以消除没有必要的锁,去除获取锁的时间。
如果一系列的连续加锁解锁操作,可能会导致不必要的性能损耗,所以引入锁粗话的概念。意思是将多个连续加锁、解锁的操作连接在一起,扩展成为一个范围更大的锁, 这个应该很好理解。
偏向锁是JDK 1.6引入的,它解决的场景是什么呢?我们大部分使用锁都是解决多线程场景下的问题,但有时候往往一个线程也会存在这样的问题,偏向锁是在单线程执行代码块时使用的机制。
锁的争夺实际上是 Monitor 对象的争夺,还有每个对象都有一个对象头,对象头是由 Mark Word 和 Klass pointer 组成的。一旦有线程持有了这个锁对象,标志位修改为 1,就进入偏向模式,同时会把这个线程的 ID 记录在对象的 Mark Word 中,当同一个线程再次进入时,就不再进行同步操作,大大减少了锁获取的时间,从而提高了性能。
我们上边提到的偏向锁,在多线程情况下如果偏向锁失败就会升级为轻量级锁, Mark Word 的结构也变为轻量级锁的结构。
执行同步代码块之前,JVM 会在线程的栈帧中创建一个锁记录(Lock Record),并将 Mark Word 拷贝复制到锁记录中。然后尝试通过 CAS 操作将 Mark Word 中的锁记录的指针,指向创建的 Lock Record。如果成功表示获取锁状态成功,如果失败,则进入自旋获取锁状态。
如果自旋锁失败,就会升级为重量级锁,也就是我们之前讲的,会把线程阻塞,需等待唤醒。
它又称为悲观锁, 升级到这种情况下,锁竞争比较激烈,占用时间也比较长,为了减少cpu的消耗,会将线程阻塞,进入阻塞队列。
synchronized就是通过锁升级策略来适应不同的场景,所以现在synchronized被优化的很好,也是我们项目中往往都会使用它的理由。
本节的内容比较多,大家好好理解,特别是锁的升级策略。本节我们提到了Lock锁,下一节,带大家深入学习一下Java的Lock 。
分享题目:面试官:有了解过Synchronized吗说说看
本文路径:http://www.shufengxianlan.com/qtweb/news36/550636.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联