在Java中,我们经常使用synchronized关键字来解决多线程的同步安全问题。更重要的是在Java面试中,synchronized属于必考点,需要掌握的知识点非常多。
接下来,我会用3节课来重点解决synchronized,让你彻底理解synchronized的使用与实现原理。
这节课,我先从synchronized的底层实现原理开讲,重点会谈到以下6点:
- Synchronized
- Synchronized方法锁、对象锁、类锁
- Synchronized的源码实现
- Synchronized的实现原理
- Monitor对象详解
- Synchronized的锁存储位置
Synchronized
Synchronized翻译为中文的意思是同步,也称之为”同步锁“,Synchronized关键字是java并发编程中必不可少的工具,它一次只允许一个线程进入特定代码段,从而避免多线程同时修改同一数据。
Synchronized的作用
在并发编程中存在线程安全问题,主要原因有:
1.存在共享数据
2.多线程共同操作共享数据
关键字synchronized可以保证在同一时刻,只有一个线程可以执行某个方法或某个代码块,同时synchronized可以保证一个线程的变化可见(可见性),即可以代替volatile。
Synchronized的三种使用方式
Java中每一个对象都可以作为锁,这是synchronized实现同步的基础:
1.普通同步方法(实例方法):锁是当前实例对象 ,进入同步代码前要获得当前实例的锁。
/** * 用在普通方法 */ private synchronized void synchronizedMethod() { System.out.println("--synchronizedMethod start--"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("--synchronizedMethod end--"); }
2.静态同步方法:锁是当前类的class对象 ,进入同步代码前要获得当前类对象的锁。
/** * 用在静态方法 */ private synchronized static void synchronizedStaticMethod() { System.out.println("synchronizedStaticMethod start"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("synchronizedStaticMethod end"); }
3.同步方法块:锁是括号里面的对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。
/** * 用在类 */ private void synchronizedClass() { synchronized (SynchronizedTest.class) { System.out.println("synchronizedClass start"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("synchronizedClass end"); } }
Synchronized的源码实现
这节课就先讲到这里,后面一节课我会重点去讲解Synchronized的锁的详细升级过程(这也是大厂经常面试的考点)。
课后作业
这是一道去阿里的面试题。
1.谈谈你synchronized 关键字的理解?
2.紧接着:再谈谈synchronized的底层实现原理?
请把你的答案写到问答区域,一课一练。(输出是最好的学习:每一次输出都会让你离目标更进一步)
synchronized是同步锁,有三种使用方法,包括普通方法、静态方法、以及代码块锁对象。
普通方法锁当前对象,静态方法锁当前类对象,代码块锁指定的对象。
锁方法底层实现是用了一个acc同步标志位来标志该方法是否加锁,代码块锁是在代码块前面和后面加了moniterenter,moniterexit指令。
虽然这两种方法实现有差异,但本质都是去获取一个moniter对象,获取到锁后,moniter对象的计数器加1,释放后,减一,如果变为0,代表锁已经完全释放,支持锁重入。
每个对象包括对象头、实例数据、对齐填充。其中对象头里的标记字段包含了锁的相关信息
核心点都谈到了,synchronized的底层实现还有一个非常关键的点就是:锁的升级与优化,这个也是经常考察的点,刚好今天我讲到了这点,回头再把这个点补下就更好了 ✗咧嘴笑✗
synchronized是java的关键字,当它用来修饰一个方法或者代码块的时候,能够保证在同一时刻只有一个线程执行该段代码。JDK1.5之后引入了自旋锁、锁粗化、轻量级锁、偏向锁来优化关键字的性能。
synchronized的底层实现是通过一个monitor的对象来完成,具体的指令有monitorenter和monitorexit。
synchronized的锁优化特别重要的,严格来讲顺序是:无锁->偏向锁->轻量级锁->重量级锁,从轻量级锁到重量级锁会涉及到你上面提到的两个核心点:自旋锁与自适应自旋锁,刚好今天我讲了锁的升级,作业也提到了这点,回头可以作业输出后,你会有新的收获,路正银加油 ✗拳头✗
synchronized是java提供的一个同步锁的关键字,java使用这个关键字来实现对代码块的加锁操作,从而保证在多线程的情况下只有一个线程能访问到被synchronized包住的代码块。
synchronized根据使用位置的不同分为普通方法锁,静态方法锁和代码块锁,锁的对象分别是当前实例对象,当前类对象和自定义对象。
虽然synchronized使用位置不同,但是底层的实现都是通过获取对象的监视器monitor来实现,在被synchronized标记的代码前后,会插入monitorenter和monitorexit指令。monitorenter会让当前线程获取用户对象的锁,如果获取成功,monitor的计数器就+1,这样别的线程就无法再获取到锁,当前线程执行sychronized的代码块,执行完成代码后会执行monitorexit,将monitor的计数器-1,当monitor的计数器=0的时候就代表解锁成功,允许别的线程获取锁。