• volatile和synchronized


    并发编程的3个特点分别是:原子性、可见性、有序性

    1. volatile

    作用:轻量级并发编程锁,解决内存可见性问题、防止指令重排序(有序性);

    1. 内存可见性
    public class volatileTest {
        private static volatile boolean flag = false;
        public static void main(String[] args) {
            Thread t1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    // 如果 flag 变量为 true 就终止执行
                    while (!flag) {
                    }
                    System.out.println("终止执行");
                }
            });
            t1.start();
            // 1s 之后将 flag 变量的值修改为 true
            Thread t2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("设置 flag 变量的值为 true!");
                    flag = true;
                }
            });
            t2.start();
        }
    }
    // 设置flag变量的值为true!
    // 终止执行
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    如果没有设置volatile,程序不会结束执行

    1. 防止指令重排序(有序性)
    public class Singleton {
        private Singleton() {}
        // 使用 volatile 禁止指令重排序
        private static volatile Singleton instance = null;
        public static Singleton getInstance() {
            if (instance == null) { // ①
                synchronized (Singleton.class) {
                    if (instance == null) {
                        instance = new Singleton(); // ②
                    }
                }
            }
            return instance;
        }
    }
    /* 懒汉式需要使用volantile来防止指令重排序
     ②处执行时,指令是有可能触发重排序的,这里实际执行分为3步
    1. 创建内存空间
    2. 在内存空间中初始化对象Singleton
    3. 将内存地址赋值给 instance对象(执行此步骤就不等于null了)
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    volatile的第2点作用一般常见的场景也是单例模式下的场景

    2. synchronized

    作用
    专业:如果一个对象对多个线程可见,则对该对象变量的所有读取和写入都是通过同步方法完成的

    通俗:能够保证在同一个时刻最多只有一个线程执行该段代码,以达到保证并发安全的效果


    对象锁:

    • 同步代码块锁:this或者自定义锁对象
    • 方法锁:默认锁对象为this

    类锁:

    • Class对象锁:Main.class
    • 静态锁:添加static

    原理

    public class Main{
    	int sum=0;
    	public synchronized void add(){
    		++sum;
    	}
     	public  void add2(){
           synchronized (Main.class) {
               ++sum;
           }
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    简单的阐述一下synchronized机制。通过javap -c Main.class命令查看字节码

    // synchronized 加在方法上
     public synchronized void add();
        Code:
           0: aload_0
           1: dup
           2: getfield      #2                  // Field sum:I
           5: iconst_1
           6: iadd
           7: putfield      #2                  // Field sum:I
          10: return
          
    // synchronized加在方法内
     public void add2();
        Code:
           0: ldc           #3                  // class com/monk/volatiletest/Main
           2: dup
           3: astore_1
           4: monitorenter
           5: aload_0
           6: dup
           7: getfield      #2                  // Field sum:I
          10: iconst_1
          11: iadd
          12: putfield      #2                  // Field sum:I
          15: aload_1
          16: monitorexit
          17: goto          25
          20: astore_2
          21: aload_1
          22: monitorexit
          23: aload_2
          24: athrow
          25: return
        Exception table:
           from    to  target type
               5    17    20   any
              20    23    20   any
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    通过上图自己买来看,同步方法并没有通过指令monitorentermonitorexit来实现,而是通过常量池下的ACC_SYNCHRONIZED标识符实现方法的同步;

    对同步代码块来说,则是通过monitorentermonitorexit指令控制Monitor的权限,也就是通过这俩指令控制锁的权限

  • 相关阅读:
    【GIT】解决合并冲突完整流程(带图及步骤)
    Elasticsearch:如何在 Elastic Agents 中配置 Beats 来采集定制日志
    基于JavaSwing开发模拟十字路口的红绿灯变化及车辆移动+作业要求 课程设计
    洛谷 P3128 最大流Max Flow
    springBoot 过滤器去除请求参数前后空格(附源码)
    【软考】PV 操作
    基于SpringBoot+微信小程序的医院预约叫号小程序
    【重拾C语言】七、指针(二)指针与数组(用指针标识数组、多维数组与指针、数组指针与指针数组)
    [04]Web前端进阶—JS伪数组
    类方法/静态方法
  • 原文地址:https://blog.csdn.net/qq_37776700/article/details/126845489