搜索
您的当前位置:首页正文

笔记之synchronized 关键字的了解

来源:步旅网

synchronized 解决的是多个线程访问资源的同步性问题,synchronized 可以保证被它修饰的方法或者代码块在同一时间只有一个线程可以执行。

1. synchronized的三种使用方法

1.1 修饰实例方法

  • 作用于当前对象实例加锁,进入同步代码前需要先获得 当前对象实例的锁。
synchronized void method() {
 //业务代码
}

1.2 修饰静态方法

  • 也就是给当前类加锁,会作用于当前对象的所有实例,进入同步代码前需要获得当前class的锁。
  • 因为静态成员不属于任何实例对象,是类成员(static表示的是这是该类的一个静态资源,不管new了多少个对象,只有一份)。
  • 所以,如果⼀个线程 A 调⽤⼀个实例对象的⾮静态 synchronized ⽅法,⽽线程 B 需要调⽤这个实例对象所属类的静态 synchronized ⽅法,是允许的,不会发⽣互斥现象,因为访问静态 synchronized ⽅法占⽤的锁是当前类的锁,⽽访问⾮静态 synchronized ⽅法占⽤的锁是当前实例对象锁。
synchronized void staic method() {
 //业务代码
}

1.3 修饰代码块

  • 指定加锁对象,对给定对象或者类加锁。synchronized(this | Object) 表示进入同步代码块之前需要获取给定对象的锁。synchronized(类名.class)表示进入同步代码块之前需要获取当前class的锁。
synchronized(this) {
 //业务代码
}

2. 总结

  • synchronized关键字加到static静态方法或者synchronized(类名.class)上,都是给Class类上锁。
  • synchronized关键字加到实例方法上是给对象实例上锁。
  • 尽量不要使⽤ synchronized(String a) 因为 JVM 中,字符串常量池具有缓存功能。

3. “单例模式了解吗?

/**
 * @author LanceQ
 * @version 1.0 2021/4/11
 */
public class SingletonDemo {
    //禁止指令重排volatile
    private static volatile SingletonDemo instance=null;
 //   private static SingletonDemo instance=null;

    private SingletonDemo(){
        System.out.println(Thread.currentThread().getName()+"\t 我是构造方法SingleDemo()");
    }
    //DCL(double check lock双端检锁机制)
    public static SingletonDemo getInstance() {
        if (instance == null) {
            synchronized (SingletonDemo.class){
                if(instance==null)
                    instance = new SingletonDemo();
            }
        }
        return instance;
    }

    public static void main(String[] args) {
        for (int i = 0; i <20; i++) {
            new Thread(()->{
                SingletonDemo.getInstance();
            },String.valueOf(i)).start();
        }
    }

}
  • 需要注意 instance 采⽤ volatile 关键字修饰也是很有必要。
  • 采⽤ volatile 关键字修饰也是很有必要的, instance = new Singleton(); 这
    段代码其实是分为三步执⾏:
  1. 为 instance 分配内存空间
  2. 初始化 instance
  3. 将 instance 指向分配的内存地址

但是由于 JVM 具有指令重排的特性,执⾏顺序有可能变成 1->3->2。指令重排在单线程环境下不会出现问题,但是在多线程环境下会导致⼀个线程获得还没有初始化的实例。

例如,线程 T1 执⾏了 1 和 3,此时 T2 调⽤ getInstance() 后发现 instance 不为空,因此返回instance,但此时 instance 还未被初始化。

使⽤ volatile 可以禁⽌ JVM 的指令重排,保证在多线程环境下也能正常运⾏。

4. 额外

  • synchronized 同步语句块的实现使⽤的是 monitorenter 和 monitorexit 指令,其中monitorenter 指令指向同步代码块的开始位置, monitorexit 指令则指明同步代码块的结束位置。

  • synchronized 修饰的⽅法并没有 monitorenter 指令和 monitorexit 指令,取得代之的确实是ACC_SYNCHRONIZED 标识,该标识指明了该⽅法是⼀个同步⽅法。

参考:JavaGuide面试突击版

因篇幅问题不能全部显示,请点此查看更多更全内容

Top