synchronized关键字的使用和底层原理

什么是synchronized关键字?

synchronized关键字是解决多个线程之间访问资源的同步性,可保证所修饰的代码块在任意时刻只有一个线程执行。
在Java早期版本,synchronized属于重量级锁,效率低下,因为监视器锁是依赖操作系统完成的,如果要挂起或唤醒一个线程,就需要操作系统的帮忙;而操作系统切换线程需要从用户态切换为内核态,需要较长时间。在Java6之后,Java官方对synchronized进行了JVM层面的优化。

Synchronized关键字如何使用

Synchronized有三种使用方法:

  • 修饰实例方法,作用相当于当前对象实例加锁,进入同步代码前要获得当前对象实例的锁
  • 修饰静态方法,相当于在当前类对象加锁,进入同步代码前要获得当前类对象实例的锁
  • 修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库的时候要获得给定对象的锁

具体使用:

双重校验锁实现对象单例(线程安全)

public class singleton {
	private volatile static singleton uniqueInstance;
	 
	 private Singleton() {
	 }
	 
	 public static Singleton getUniqueInstance() {
	 	//先判断对象是否实例化过,没用实例化才能进入加锁代码
	 	if (uniqueInstance == null) {
	 		//类对象加锁
	 		synchronized (Singleton.class) {
	 			if (uniqueInstance == null){
	 				uniqueInstance = new Singleton();
	 			}
	 		}
	 	}
	 	return uniqueInstance;
	 }
}

其中,uniqueInstance使用volatile关键字修饰是很有必要的。
uniqueInstance = new Singleton();这段代码分为以下三个步骤:

  1. 为uniqueInstance分配内存空间
  2. 初始化uniqueInstance
  3. 将uniqueInstance指向分配的内存地址

synchronized关键字的底层原理

synchronized的底层原理属于JVM层面
① synchronized 同步语句块的情况

public class SynchronizedDemo() {
	public void method() {
		synchronized (this) {
			System.out.println("synchronized 代码块");
		}
	}
}

通过 JDK 自带的 javap 命令查看 SynchronizedDemo 类的相关字节码信息:首先切换到类的对应目录执行 javac
SynchronizedDemo.java 命令生成编译后的 .class 文件,然后执行 javap-c-s-v-lSynchronizedDemo.class。
synchronized 同步语句块的实现使用的是 monitorenter 和monitorexit 指令,其中 monitorenter 指令指向同步代码块的开始位置,monitorexit 指令则指明同步代码块的结束位置。当执行 monitorenter 指令时,线程试图获取锁也就是获取 monitor(monitor对象存在于每个Java对象的对象头中,synchronized 锁便是通过这种方式获取锁的,也是为什么Java中任意对象可以作为锁的原因) 的持有权.当计数器为0则可以成功获取,获取后将锁计数器设为1也就是加1。相应的在执行 monitorexit 指令后,将锁计数器设为0,表明锁被释放。如果获取对象锁失败,那当前线程就要阻塞等待,直到锁被另外一个线程释放为止。
② synchronized 修饰方法的的情况

public class SynchronizedDemo2{
	public synchronized void method() {
		System.out.println("synchronized 方法");
	}
}

synchronized 修饰的方法并没有 monitorenter 指令和 monitorexit 指令,取得代之的确实是ACC_SYNCHRONIZED 标识,该标识指明了该方法是一个同步方法,JVM 通过该 ACC_SYNCHRONIZED 访问标志来辨别一个方法是否声明为同步方法,从而执行相应的同步调用。

你可能感兴趣的:(多线程,分布式,Java基础)