美文网首页
Java-synchronized

Java-synchronized

作者: Android_Gleam | 来源:发表于2020-10-09 15:43 被阅读0次

从代码入手,先看下代码

public class SyncTest {
    public int count;

    public  void get(){
        //同步代码块
        synchronized(this) {
            count = count + 1;
        }
    }

    public static void main(String[] args) {

    }
}

注意这里我们加锁的方式是同步代码块,然后反编译下class文件,看一下get方法

 public void get();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=3, locals=3, args_size=1
         0: aload_0
         1: dup
         2: astore_1
         3: monitorenter                    //重点
         4: aload_0
         5: aload_0
         6: getfield      #2                  // Field count:I
         9: iconst_1
        10: iadd
        11: putfield      #2                  // Field count:I
        14: aload_1
        15: monitorexit                     //重点
        16: goto          24
        19: astore_2
        20: aload_1
        21: monitorexit
        22: aload_2
        23: athrow
        24: return

这里我们可以看到这两行,第3行monitorenter和第15行monitorexit,如果我们把锁去掉,这两行代码就不存在了,这就是加锁的秘诀,编译器会在代码中为我们插入这两个指令。

我们可以将其看成一个对象,也就是我们拿锁所拿到的对象。synchronized就是通过这两个指令实现的。

下面我们将上面的同步代码块改为:

public synchronized void get() {
        count = count + 1;
    }

然后在看下编译后的class文件:

 public synchronized void get();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_SYNCHRONIZED  //重点
    Code:
      stack=3, locals=1, args_size=1
         0: aload_0
         1: aload_0
         2: getfield      #2                  // Field count:I
         5: iconst_1
         6: iadd
         7: putfield      #2                  // Field count:I
        10: return

我们发现那两个指令不见了,flags中多了ACC_SYNCHRONIZED,将方法标记成了一个同步方法。虽然在字节码中我们看不到那两条指令了,但是底层实现也是同样的原理。

原理

使用monitorenter和monitorexit指令实现的

  • monitorenter指令是在编译后插入到同步代码块的开始位置,而monitorexit是插入到方法结束处和异常处
  • 每个monitorenter必须有对应的monitorexit与之配对
  • 任何对象都有一个monitor与之关联

synchronized的优化(JDK1.6开始)

重量级锁

访问同步代码块的时候,竞争失败的线程会被挂起,发生上下文切换,每次上下文切换大约耗费3-5微秒,挂起一起,唤醒一次。
缺点:线程阻塞,响应时间慢。
使用场景:适用于追求吞吐量,同步代码块执行速度较长。

轻量级锁

CPU执行一条指令的时间基本在0.6纳秒,假设我们的同步代码块中的逻辑很简单,执行速度很快,则不将线程挂起,通过CAS操作来加锁和解锁,避免上下文切换。
自适应自旋锁(概念):过度的自旋操作也会造成CPU的资源浪费,控制自旋次数,由虚拟机自行进行判定,动态进行调整,不会超过一个上下文切换所需的时间,如果超过,则将升级为重量级锁。
缺点:如果始终得不到锁,竞争的线程使用自旋会消耗CPU。
使用场景:适用于追求响应时间,同步代码块执行速度非常快。

偏向锁

大多数情况下,锁不仅不存在多线程竞争,而且总是由同一线程多次获得,为了让线程获得锁的代价更低而引入了偏向锁。无竞争时不需要进行CAS操作来加锁和解锁。第一次拿锁通过CAS操作,之后在拿锁判断上次拿锁的是不是自己,如果是自己就直接用,不需要在进行CAS操作。如果有竞争则升级为轻量级锁,会导致用户线程全部被挂起,竞争激烈时不可用。
缺点:如果线程间存在竞争,会带来额外的锁撤销的消耗。
使用场景:适用于只有一个线程访问同步块的场景。

相关文章

  • Java-synchronized

    synchronized是java的关键字(内置的),Lock是java的接口。两者都实现了对于临界资源的同步互斥...

  • Java-synchronized

    从代码入手,先看下代码 注意这里我们加锁的方式是同步代码块,然后反编译下class文件,看一下get方法 这里我们...

  • java-synchronized记录

    总结:所有的 synchronized 都是作用在对象上的。 参考 https://juejin.im/post/...

  • java-synchronized 理解

    同步代码块里面是原子操作,synchronized保护原子操作防止被打断,o代表是当前对象,synchronize...

  • [Java]重学Java-synchronized

    synchronized的作用 synchronized作为Java提供的锁关键字,在单进程的时候可以提供互斥的功...

  • 深入理解Java-synchronized关键字

    理解Java中的synchronized关键字 问题思考:我们可以带着问题理解 synchronized 1.理解...

网友评论

      本文标题:Java-synchronized

      本文链接:https://www.haomeiwen.com/subject/tgylpktx.html