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!
// 终止执行

如果没有设置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了)
*/

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;
       }
   }
}

简单的阐述一下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
}

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

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

你可能感兴趣的:(java,单例模式,开发语言)