当前位置: 首页 > article >正文

【Java】【并发编程】Synchronized

在 Java 中,关键字 synchronized可以保证在同一个时刻,只有一个线程可以执行某个方法或者某个代码块(主要是对方法或者代码块中存在共享数据的操作),同时我们还应该注意到synchronized另外一个重要的作用,synchronized可保证一个线程的变化(主要是共享数据的变化)被其他线程所看到(保证可见性,完全可以替代Volatile功能)。

特点

  • 保证原子性:通过地城JVM实现,同一时间只有一个线程运行锁定代码块
  • 保证可见性,通过内存屏障(Load&store)实现,通过monitorenter加锁,monitorexit解锁,内部共享变量每次读取都强制从主内存读取最新值,释放锁后共享变量数据变更,强制刷新会主内存
  • 保证有序性:monitorenter加锁之后,同一时间只能被一个线程访问,即认为单线程执行,可以保证有序性,但无法禁止指令重排
  • 无法禁止指令重排:在代码块中,相当于单线程执行,单代码块的执行顺序可以指令重排
  • 通过Monitor管程监视器锁对象或ACC_SYNCHRONIZED修饰符实现加锁
    • monitorenter指令加锁,计数器+1
    • monitorexit指令释放锁,计数器-1
    • 线程解锁前,必须把共享变量的最新值刷新到主内存。
    • 线程加锁时,将清空工作内存中共享变量的值,从而使用共享变量时需要从主内存中重新读取最新的值(注意,加锁与解锁是同一把锁)

实现(锁范围)

  • 修饰普通方法,当前对象锁
  • 修饰静态方法,类锁,所有类对象共用同一把锁
  • 修饰实例变量,仅对指定的对象加锁
   // 只能对不可变的共享对象加锁
   private final Object demo = new Demo();

   public synchronized void addNumber(long num){ // 对当前方法加锁,等同synchronized(this){}
      demo.count += num;
   }

   public static synchronized void addNumber_1(long num){ // 对类下所有对象加锁
      demo.count += num;
   }

   public void addNumber2(long num){
      synchronized (demo){ // 仅对demo加锁
         demo.count += num;
      }
   }

原理

  • 同步实例,使用monitor管程对象管理,monitorenter加锁和monitorexit释放锁
  • 同步方式,使用运行时常量池中方法的 ACC_SYNCHRONIZED 标志来隐式实现的
    • 静态方法还需要ACC_STATIC标志

修饰实例

    private final Demo DEMO = new Demo();
    public void test(){
        synchronized (DEMO){
            DEMO.test1(); // test1是Demo的普通方法,无需关注
        }
    }
  public void test();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=1
         0: aload_0
         1: getfield      #4                  // Field DEMO:Lcom/dongle/demo/jvm/lock/LockDemo$Demo;
         4: dup
         5: astore_1
         6: monitorenter
         7: aload_0
         8: getfield      #4                  // Field DEMO:Lcom/dongle/demo/jvm/lock/LockDemo$Demo;
        11: invokestatic  #5                  // Method com/dongle/demo/jvm/lock/LockDemo$Demo.access$100:(Lcom/dongle/demo/jvm/lock/LockDemo$Demo;)V
        14: aload_1
        15: monitorexit
        16: goto          24
        19: astore_2
        20: aload_1
        21: monitorexit
        22: aload_2
        23: athrow
        24: return
      Exception table:
         from    to  target type
             7    16    19   any
            19    22    19   any
      LineNumberTable:
        line 13: 0
        line 14: 7
        line 15: 14
        line 16: 24
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      25     0  this   Lcom/dongle/demo/jvm/lock/LockDemo;
      StackMapTable: number_of_entries = 2
        frame_type = 255 /* full_frame */
          offset_delta = 19
          locals = [ class com/dongle/demo/jvm/lock/LockDemo, class java/lang/Object ]
          stack = [ class java/lang/Throwable ]
        frame_type = 250 /* chop */
          offset_delta = 4
  1. 通过monitorenter加锁:6: monitorenter
  2. 通过monitorexit释放锁:15: monitorexit

修饰方法

    private final Demo DEMO = new Demo();
    public synchronized void test(){
        DEMO.test1(); // test1是Demo的普通方法,无需关注
    }
public synchronized void test();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_SYNCHRONIZED
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: getfield      #4                  // Field DEMO:Lcom/dongle/demo/jvm/lock/LockDemo$Demo;
         4: invokestatic  #5                  // Method com/dongle/demo/jvm/lock/LockDemo$Demo.access$100:(Lcom/dongle/demo/jvm/lock/LockDemo$Demo;)V
         7: return
      LineNumberTable:
        line 13: 0
        line 14: 7
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       8     0  this   Lcom/dongle/demo/jvm/lock/LockDemo;
  1. 通过方法修饰符ACC_SYNCHRONIZED隐式实现

http://www.kler.cn/a/6652.html

相关文章:

  • MySQL数据库——门诊管理系统数据库数据表
  • qlib优缺点
  • uniapp自定义树型结构数据弹窗,给默认选中的节点,禁用所有子节点
  • 【读书笔记】《论语别裁》学而有何乐
  • 单元测试使用记录
  • 将 Matplotlib 图形转换为 PIL 图像并返回
  • 警惕,3月20日WOS目录更新,50本SCI/SSCI被剔除,这个出版社多达18本
  • PX4从放弃到精通(二十七):固定翼姿态控制
  • Java:5大最佳Java库
  • 跳表skiplist
  • Android12 Launcher3客制化:添加非抽屉模式(可动态切换)、图标自动补位功能
  • 使用C#中的Aspose Word 在对Word文档中的书签详细总结(包括增删改查等操作)
  • python实战应用讲解-【numpy专题篇】numpy常见函数使用示例(十)(附python示例代码)
  • 2023年美赛春季赛 赛题浅析
  • 在Linux系统下使用GDB调试C++程序【命令行调试与vscode编译器调试】
  • django前后端不分离——图片验证码,基于Bootstrap3的自定义分页器,Bootstrap模态框无法使用解决方案
  • list模拟实现
  • 00后面试华为软件测试工程师,竭尽全力拿到15K。。。。。
  • 解析安装程序使用指南
  • 华为OD机试-最优资源分配-2022Q4 A卷-Py/Java/JS
  • 美团暑期实习
  • 【python设计模式】10、组合模式
  • 从大厂到创业公司,管理上需要怎样转变?
  • ChatGPT背后的技术和多模态异构数据处理的未来展望——我与一位资深工程师的走心探讨
  • 获取元素通常使用的两种方式
  • 【华为机试真题详解JAVA实现】—坐标移动