java并发--synchronized原理

相信对于各位java大佬,synchronized关键字大家可能并不陌生,今天这边来详细聊聊这个synchronized关键字。

相信有有写java并发基础的同学可能知道,synchronized关键字是利用锁的机制来实现代码同步的。对于java中的锁机制有如下两个特性(还是要了解下的,吹牛逼的时候好提升下b格)

互斥性:即在同一时间只允许一个线程持有某个对象锁,通过这种特性来实现多线程中的协调机制,这样在同一时间只有一个线程对需同步的代码块(复合操作)进行访问。互斥性我们也往往称为操作的原子性。
可见性:必须确保在锁被释放之前,对共享变量所做的修改,对于随后获得该锁的另一个线程是可见的(即在获得锁时应获得最新共享变量的值),否则另一个线程可能是在本地缓存的某个副本上继续操作从而引起不一致。

关于synchronized关键字,有如下几种用法:
根据修饰对象分类
1、同步方法
(1) 同步非静态方法
Public synchronized void methodName(){
……
}
(2) 同步静态方法
Public synchronized static void methodName(){
……
}
在这里插入图片描述
2、同步代码块
synchronized(this|object) {}
synchronized(类.class) {}
Private final Object MUTEX =new Object();
Public void methodName(){
Synchronized(MUTEX ){
……
}
}
在这里插入图片描述
关于这两种方式具体有什么区别呢?

在说这个之前,我们先来讲讲对象锁和类锁:
(1)获取对象锁(修饰非静态方法):synchronized(this|object) {}
在 Java 中,每个对象都有一个 monitor 对象,这个对象其实就是 Java 对象的锁,通常被称为“内置锁”或“对象锁”。类的对象可以有多个,所以每个对象有其独立的对象锁,互不干扰。
(2)获取类锁(修饰静态方法):synchronized(类.class) {}
在 Java 中,针对每个类也有一个锁,可以称为“类锁”,类锁实际上是通过对象锁实现的,即类的 Class 对象锁。每个类只有一个 Class 对象,所以每个类只有一个类锁。

在 Java 中,每个对象都会有一个 monitor 对象,监视器。

  1.     某一线程占有这个对象的时候,先monitor 的计数器是不是0,如果是0还没有线程占有,这个时候线程占有这个对象,并且对这个对象的monitor+1;如果不为0,表示这个线程已经被其他线程占有,这个线程等待。当线程释放占有权的时候,monitor-1;
    
  2.     同一线程可以对同一对象进行多次加锁,+1,+1,重入性,因此synchronized也可以理解为一把可重入锁
    

下面我么便来验证下上面的,这里介绍一个java反编译命令:javap -v

baomw@baomingwudeMacBook-Air:~/IdeaProjects/test1/target/classes/com/baomw$     javap -v SyncTest.class
Classfile /Users/baomw/IdeaProjects/test1/target/classes/com/baomw/SyncTest.class
  Last modified 2019-3-17; size 763 bytes
  MD5 checksum d728833a93ce9cf96516976dd96d5ed7
  Compiled from "SyncTest.java"
public class com.baomw.SyncTest
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #5.#29         // java/lang/Object."<init>":()V
   #2 = Fieldref           #4.#30         // com/baomw/SyncTest.num:I
   #3 = Methodref          #4.#31         // com/baomw/SyncTest.add:()V
   #4 = Class              #32            // com/baomw/SyncTest
   #5 = Class              #33            // java/lang/Object
   #6 = Utf8               num
   #7 = Utf8               I
   #8 = Utf8               <init>
   #9 = Utf8               ()V
  #10 = Utf8               Code
  #11 = Utf8               LineNumberTable
  #12 = Utf8               LocalVariableTable
  #13 = Utf8               this
  #14 = Utf8               Lcom/baomw/SyncTest;
  #15 = Utf8               add
  #16 = Utf8               remove
  #17 = Utf8               StackMapTable
  #18 = Class              #32            // com/baomw/SyncTest
  #19 = Class              #33            // java/lang/Object
  #20 = Class              #34            // java/lang/Throwable
  #21 = Utf8               main
  #22 = Utf8               ([Ljava/lang/String;)V
  #23 = Utf8               args
  #24 = Utf8               [Ljava/lang/String;
  #25 = Utf8               MethodParameters
  #26 = Utf8               <clinit>
  #27 = Utf8               SourceFile
  #28 = Utf8               SyncTest.java
  #29 = NameAndType        #8:#9          // "<init>":()V
  #30 = NameAndType        #6:#7          // num:I
  #31 = NameAndType        #15:#9         // add:()V
  #32 = Utf8               com/baomw/SyncTest
  #33 = Utf8               java/lang/Object
  #34 = Utf8               java/lang/Throwable
{
  public com.baomw.SyncTest();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 12: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/baomw/SyncTest;

  public static synchronized void add();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED
    Code:
      stack=2, locals=0, args_size=0
         0: getstatic     #2                  // Field num:I
         3: iconst_1
         4: iadd
         5: putstatic     #2                  // Field num:I
         8: return
      LineNumberTable:
        line 18: 0
        line 19: 8

  public void remove();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=1
         0: aload_0
         1: dup
         2: astore_1
         3: monitorenter
         4: getstatic     #2                  // Field num:I
         7: iconst_1
         8: isub
         9: putstatic     #2                  // Field num:I
        12: aload_1
        13: monitorexit
        14: goto          22
        17: astore_2
        18: aload_1
        19: monitorexit
        20: aload_2
        21: athrow
        22: return
      Exception table:
         from    to  target type
             4    14    17   any
            17    20    17   any
      LineNumberTable:
        line 23: 0
        line 24: 4
        line 25: 12
        line 26: 22
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      23     0  this   Lcom/baomw/SyncTest;
      StackMapTable: number_of_entries = 2
        frame_type = 255 /* full_frame */
          offset_delta = 17
          locals = [ class com/baomw/SyncTest, class java/lang/Object ]
          stack = [ class java/lang/Throwable ]
        frame_type = 250 /* chop */
          offset_delta = 4

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=0, locals=1, args_size=1
         0: invokestatic  #3                  // Method add:()V
         3: return
      LineNumberTable:
        line 29: 0
        line 30: 3
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       4     0  args   [Ljava/lang/String;
    MethodParameters:
      Name                           Flags
      args

  static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=1, locals=0, args_size=0
         0: iconst_0
         1: putstatic     #2                  // Field num:I
         4: return
      LineNumberTable:
        line 15: 0
}
SourceFile: "SyncTest.java"

测试代码发编译后的指令如上:
在这里插入图片描述
如上,在计算机指令中,当synchronized修饰我们的方法时,在方法入口增加ACC_SYNCHRONIZED 对整个方法加锁
而在修饰我们的代码块时,在代码块的执行入口和出口处增加Monitorenter和
Monitorexit,注意这两个总是成对出现的。

当然最后在使用我们的synchronized的时候,以下问题还是需要注意下的:
(1)与moniter关联的对象不能为空
这个应该不用多说,因为我们的每一个java对象都和一个monitor关联,既然对象都为空了,monitor也就无从谈起了。
(2)synchronized作用域太大
在使用中应该尽可能减少锁的粒度,不然作用域太大,作用域内的代码很可能还会出现并发问题,其次,代码都变串行执行,效率低
(3)不同的monitor企图锁相同的方法
(4)多个锁的交叉导致死锁

好了关于synchronized关键子暂时先讲到这里,后续有相关知识再来补充。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值