前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >深入理解synchronized

深入理解synchronized

作者头像
贪挽懒月
发布2021-06-21 20:14:49
3980
发布2021-06-21 20:14:49
举报
文章被收录于专栏:JavaEE

面试官:请谈谈你对synchronized的理解。

小白:这是一个java的关键字,用来控制并发的,被它锁住的代码同一时刻只能有一个线程访问。

面试官:还有吗?

小白:没有了……

面试官:那你先回去等通知吧!


synchronized,相信学过java的都知道它,但是面试一被问到这个,又总是答不出多少东西来。下面我就将synchronized的知识点列举出来,深入理解(要深入它,才能征服它)。


1. 用来干嘛的?

这是一个同步关键字,保证同一时刻只能有一个线程执行被其修饰的方法或代码块,可以保证线程安全。

2. 怎么用呢?

这是一个关键字,可以用来修饰静态方法、实例方法、代码块。注意这里的代码块不是类中的静态代码块和构造代码块,而是方法中的代码块。

  • 修饰静态方法: 介绍它修饰静态方法之前,先来回忆一下静态方法的特点。静态是该类所有实例共享的,JVM加载该类时就会对其进行初始化,因为不属于任何一个实例,所以静态方法里面不能用this关键字。如果synchronized修饰静态方法,那么锁对象是啥呢?首先排除this,因为调用静态方法的时候可能该类都还没有实例。所以修饰静态方法的时候,锁对象其实是当前class。
代码语言:javascript
复制
// 静态方法
public synchronized static void staticFun(){
    System.out.println("synchronized修饰静态方法,锁对象是当前class");
    // 业务代码……
}
  • 修饰实例方法: 既然都说了是实例方法,那么锁对象就是当前类的实例。
代码语言:javascript
复制
// 实例方法
public synchronized void instanceFun(){
    System.out.println("synchronized修饰实例方法,锁对象是类实例");
    // 业务代码……
}
  • 修饰代码块: 修饰代码块,锁对象可以是class,也可以是给定的对象。如果是class,那就是不管该类new几个实例,都是属于这个类的,都会被锁住;如果是对象,那么不同对象去访问时是可以获取到锁的,所以class作为锁其实粒度更粗。
代码语言:javascript
复制
public void fun(){
    synchronized (TestSync.class){ // 锁对象是当前class
//    synchronized (this){ // 锁对象是实例
        System.out.println("synchronized修饰代码块,锁对象可以是实例,可以是类");
    }
}

3. 线程A调用类的同步实例方法,线程B可以同时调用类的同步静态方法吗?为什么?

我们先用代码看结果,再解释为什么。

代码语言:javascript
复制
 // 静态方法
 public synchronized static void staticFun(){
     System.out.println("synchronized修饰静态方法,锁对象是当前class");
     System.out.println(Thread.currentThread().getName() + "进入同步静态方法");
     System.out.println(Thread.currentThread().getName() + "执行结束");
 }

 // 实例方法
 public synchronized void instanceFun(){
     System.out.println("synchronized修饰实例方法,锁对象是类实例");
     System.out.println(Thread.currentThread().getName() + "进入同步实例方法");
     try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); }
     System.out.println(Thread.currentThread().getName() + "执行结束");
 }

 public static void main(String[] args){
     TestSync testSync = new TestSync();
     new Thread(() -> {
         testSync.instanceFun();
     }, "线程A").start();

     new Thread(() -> {
         staticFun();
     }, "线程B").start();

     new Thread(() -> {
         testSync.instanceFun();
     }, "线程C").start();
 }

运行结果:

运行结果

上面的代码,线程A调用实例方法,并且进入方法后线程睡了5秒钟;线程B调用静态方法,还没等线程A结束,线程B已经执行结束了,线程B不需要等线程A释放锁也可以执行。而线程C,因为是同一个对象去调用的同步实例方法,所以得等线程A释放了锁,线程C才能拿到执行权。假如线程C是另外再new一个对象去调用的,那么也不需要等待线程A释放锁。

从结果可以得出答案:线程A调用类的同步实例方法,线程B可以同时调用类的同步静态方法。原因就是同步实例方法的锁是对象锁,而同步静态方法的锁是类锁,锁对象不同,所以可以同时调用

4. 可以用String字符串来做锁对象吗?

可以,但没必要。代码块的锁对象其实可以是任意对象,不过一般都用class或者this,并不建议用string做锁对象,因为用string很容易造成死锁。为什么容易造成死锁呢?因为JVM中有个常量池,比如你定义两个字符串:

代码语言:javascript
复制
String str1 = "haha";
String str2 = "haha";

这里明明是两个字符串,但其实是同一个对象,因为这样赋值的String,首先会看常量池中有没有,没有就往常量池中添加一个,并指向它,有的话,就直接指向。所以str1和str2都是指向常量池中同一个对象。

5. synchronized可以修饰构造方法吗?为什么?

不能修饰构造方法,构造方法只能有权限修饰符,比如public、private之类的,它本身就是线程安全的。

6. jdk1.6开始对synchronized做了哪些优化?

jdk1.6之前,synchronized是很重的锁,jdk1.6开始,做了大量的优化,比如用偏向锁、轻量级锁、自旋锁、适应性自旋锁、锁消除、锁粗化等技术来减少锁操作的开销(当你这么回答的时候,估计面试官紧接着就问什么是偏向锁、自旋锁……有什么区别,这些后续再详细地说)。

7. 你知道synchronized的底层原理吗?

  • 同步代码块:方法里面的同步代码块,synchronized底层是通过监视器monitor来实现的。通过指令
代码语言:javascript
复制
javap -c -s -v -l Xxx.class

可以发现monitorenter指令指向同步代码块开始的位置,同时会尝试获取锁,锁的计数器为0表示可以获取锁,获取后计数器变为1;monitorexit指令指向同步代码块结束的位置,同时释放锁,将锁的计数器置为0。所以获取锁就是获取Monitor的执行权。Monitor是基于C++,由ObjectMonitor实现的,每个对象都内置了ObjectMonitor。另外,wait/notify方法也是基于monitor来实现的。

  • 同步方法:执行上述的javap指令查看同步方法,可以发现并没有monitorenter和monitorexit指令,但是在方法开头有个名为ACC_SYNCHRONIZED的flag标识,同步方法就是通过这个标识来控制同步操作的。

8. synchronized和ReentrantLock有何异同?

相同点:

  • 两者都是可重入锁;
  • 都可实现选择性通知;

不同点:

  • synchronized是JVM层面的,ReentrantLock是API层面的;
  • synchronized是非公平锁,ReentrantLock可以指定为公平锁或者非公平锁;
  • synchronized无需手动释放锁,ReentrantLock需要手动释放锁;
  • synchronized等待不能中断,ReentrantLock等待可通过lock.lockInterruptibly()中断等待;
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档