前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java并发——synchronized锁(六)

Java并发——synchronized锁(六)

原创
作者头像
翰墨飘香
修改2024-04-21 07:55:33
670
修改2024-04-21 07:55:33
举报
文章被收录于专栏:Java并发Java并发

一、定义

是一种用于实现线程间同步的Java关键字,主要目的是确保在多线程环境下,对共享资源的访问是安全的,避免出现数据不一致的问题。就是保证在同一时刻,被synchronized修饰的方法或代码块只有一个线程在执行,其他线程必须等待,解决并发安全问题

参考

https://www.cnblogs.com/zys2019/p/17265104.html

https://zhuanlan.zhihu.com/p/410056254?utm_medium=social&utm_oi=733685299476992000

https://www.cnblogs.com/semi-sub/p/12906660.html

https://juejin.cn/post/7046921350065160206

二、分类

synchronized的锁可分为类锁和对象锁。

1)类锁

是用来锁类的,让所有对象共同争抢一把锁。一个类的所有对象共享一个class对象,共享一组静态方法,类锁的作用就是使持有者可以同步地调用静态方法。当synchronized修饰静态方法或者class对象的时候,拿到的就是类锁。

2)对象锁

是用来锁对象的,虚拟机为每个的非静态方法和非静态域都分配了自己的空间,每个对象各有一把锁,即同一个类如果有两个对象就有两把锁。synchronized修饰非静态方法或者this时拿到的就是对象锁。

三、用法

1、修饰静态方法,锁作用于类的Class实例

synchronized放在类方法上,也就是我们所说的静态方法上,锁对象是方法区中的类对象,是一个全局锁。

当对静态方法加锁后,就使用了类锁,这个类的所有对象共享这个静态方法,这个方法就是同步方法。换句话说,就是当一个线程执行一个对象中的同步方法时,其他线程调用同一对象上的同步方法将会被阻塞,直到第一个线程完成使用这个对象。

代码语言:java
复制
 public class AddUtil {
    private static int i = 0;   //共享资

    public synchronized static void synBlock() {

        for (int j = 0; j < 10000; j++) {
            i++;
        }
        System.out.println(i);

    }
}

2、修饰普通方法,锁作用于当前对象实例

synchronized放在实例方法上,锁对象是当前的this对象

代码语言:java
复制
public class fancySyncTest {
    public synchronized void method1(){
        try {
            System.out.println(Thread.currentThread().getName());
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] agrs) throws InterruptedException {
        final fancySyncTest fs = new fancySyncTest();

        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                fs.method1();
            }
        },"线程1获取到资源");

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                fs.method1();
            }
        },"线程2获取到资源");

        t1.start();
        t2.start();
    }
}

3、修饰代码块,作用于当前对象实例,需要指定加锁对象

synchronized修饰代码块,也就是synchronized(object){},锁对象是()中的对象

代码语言:java
复制
public class TestNotes {
	private static Object object;
	
    public String decStock() {
        synchronized (object) {
            //todo
        }
        return "OK";
    }
}

四、同步原理

针对synchronized修饰的地方不同,实现的原理不同

1、修饰代码块底层原理

是基于 Java 对象头中的监视器锁(Monitor)实现的;最终依赖操作系统的Mutex lock(互斥锁)来实现的。

每个Java对象都可以用作一个实现同步的锁,这种锁被称为内置锁或对象锁或者Monitor锁。当一个线程进入一个synchronized方法或代码块时,它会尝试获取该对象的锁;如果锁已被其他线程持有,则该线程将阻塞,直到锁被释放。一旦线程获得了锁,它就可以执行synchronized代码块中的操作,并在退出代码块时释放锁(无论是正常路径退出,还是通过抛出异常退出)。

代码语言:java
复制
public class SynchronizedDemo {
    private static int i = 0;   //共享资
    public void synBlock() {
        synchronized (this) {
            for (int j = 0; j < 10000; j++) {
                i++;
            }
        }
    }
}

使用jclasslib查看字节码文件,可以看出,synchronized 代码块实际上多了 monitorenter 和 monitorexit 指令。

代码语言:java
复制
      0: aload_0
         1: dup
         2: astore_1
         3: monitorenter
         4: iconst_0
         5: istore_2
         6: iload_2
         7: sipush        10000
        10: if_icmpge     27
        13: getstatic     #2                  // Field i:I
        16: iconst_1
        17: iadd
        18: putstatic     #2                  // Field i:I
        21: iinc          2, 1
        24: goto          6
        27: aload_1
        28: monitorexit
        29: goto          37
        32: astore_3
        33: aload_1
        34: monitorexit
        35: aload_3
        36: athrow
        37: return

对于同步代码块来说,主要使用monitor(监视器)实现的,就相当于一个房间一次只能被允许一个进程进入。主要包含monitorenter和monitorexit。其中monitorenter指向同步代码块开始的位置,monitorexit指向同步代码块结束的位置。当执行monitorenter时,线程试图monitor的持有权,当monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者。若其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权。当执行monitorexit时,monitor的进入数减1,如果减1后进入数为0,那线程退出monitor。

2、修饰方法底层原理

对于同步方法来说,使用的是ACC_SYNCHRONIZED 标识,通过该标识指明该方法是否是一个同步方法

被 synchronized 修饰的方法会有一个 ACC_SYNCHRONIZED 标志。当某个线程要访问某个方法的时候,会首先检查方法是否有 ACC_SYNCHRONIZED 标志,如果有则需要先获得 monitor 锁,然后才能开始执行方法,方法执行之后再释放 monitor 锁。其他方面, synchronized 方法和刚才的 synchronized 代码块是很类似的,例如这时如果其他线程来请求执行方法,也会因为无法获得 monitor 锁而被阻塞。

修饰实例方法和修饰类方法都是ACC_SYNCHRONIZED标识符去实现的。只是它们锁住的对象不同

代码语言:java
复制
public static  synchronized void synBlock() {
        System.out.println(100);
    }

运行 javap -v SynchronizedDemo.class

代码语言:java
复制
{
  public org.java.basic.concurret.basic.SynchronizedDemo();
    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 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lorg/java/basic/concurret/basic/SynchronizedDemo;

  public static synchronized void synBlock();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED
    Code:
      stack=2, locals=0, args_size=0
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: bipush        100
         5: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
         8: return
      LineNumberTable:
        line 8: 0
        line 10: 8
}

五、底层原理

5.1 java对象

提到的monitor监视器对象存在于Java对象的对象头Mark Word中

java对象结构:

  1. 对象头 :比较复杂,synchronized加的锁,就保存在这里面的某些数据位。对象头大小是固定的;对象头结构如下图:
  2. 实例数据 :就是对象的业务数据;存放类的属性数据信息,包括父类的属性信息。如果是数组,那么实例部分还包括数组的长度,这部分内存按4字节对齐。
  3. 对齐填充位 :64位jvm,默认需要对象大小必须位8byte(字节)的整数倍,所以有时候需要对齐填充位。填充数据不是必须存在的,仅仅是为了字节对齐

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

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

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、定义
  • 二、分类
  • 三、用法
    • 1、修饰静态方法,锁作用于类的Class实例
      • 2、修饰普通方法,锁作用于当前对象实例
        • 3、修饰代码块,作用于当前对象实例,需要指定加锁对象
        • 四、同步原理
          • 1、修饰代码块底层原理
            • 2、修饰方法底层原理
            • 五、底层原理
              • 5.1 java对象
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档