synchronized 关键字

一、synchronized 介绍

synchronized 是 JVM 内置锁,通过内部对象 Monitor (监视器锁)来实现,基于进入与退出 monitor 对象来实现方法与代码块的同步。

监视器锁的实现最终依赖操作系统的 Mutex lock(互斥锁)来实现。

synchronized 关键字_第1张图片

1.1 monitorenter

每个对象有一个监视器锁(monitor)。当 monitor 被占用时就会处于锁定状态,线程执行 monitorenter 指令时尝试获取 monitor 的所有权,过程如下:

  1. 如果 monitor 的进入线程数为 0,则该线程进入 monitor,然后将进入数设置为 1,该线程即为 monitor 的所有者。
  2. 如果线程已经占有该 monitor,只是重新进入,则进入 monitor 的进入数加 1。
  3. 如果其他线程已经占用了 monitor,则该线程进入阻塞状态,直到 monitor 的进入数为 0,再重新尝试获取 monitor 的所有权。

1.2 monitorexit

执行 monitorexit 必须是 objectref 所对应的 monitor 的所有者。
指令执行时,monitor 的进入数减 1,如果减 1 后进入数为 0,那线程退出 monitor,不再是这个 monitor 的所有者。其他被这个 monitor 阻塞的线程可以尝试去获取这个 monitor 的所有权。

二、synchronized 加锁方式

synchronized 主要有 3 种加锁方式

  1. 修饰实例方法,作用于当前实例加锁,进入同步代码前要获得当前实例的锁
  2. 静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁
  3. 修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。

2.1 修饰实例方法

public class MyClass {
    public synchronized void instanceMethod() {
        // 这里是同步代码块
    }
}

作用于当前实例加锁,进入同步代码前要获得当前实例的锁。

2.2 静态方法

public class MyClass {
    public static synchronized void staticMethod() {
        // 这里是同步代码块
    }
}
// 或者
public class ClassName {
   public void method() {
      synchronized(ClassName.class) {
         // 这里是同步代码块
      }
   }
}

作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁

2.3 修饰代码块

public class TestClass{
	private static Object object;
	public void test(){
		synchronized(object){
			//这里是同步代码块
		}
	}
}
// 或者
public void test(){
	synchronized(this){
		//这里是同步代码块
	}
}

指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。

三、Java 对象组成

synchronized 关键字_第2张图片

3.1 Java 对象由三部分组成

  • 对象头:比较复杂,synchronized 加的锁,就保存在这里面的某些数据位。对象头大小是固定的。
  • 实例数据:就是对象的业务数据。
  • 对齐填充位:64 位 jvm,默认需要对象大小必须8byte(字节)的整数倍,所以有时候需要对齐填充位。

3.2 Mark Word

锁的不同状态,就是存在对象头中的Mark Word区域中。

下图是32位系统的Mark Word的区域具体分布:
synchronized 关键字_第3张图片
64位 Mark Word
synchronized 关键字_第4张图片

四、锁的升级过程

synchronized 锁有如下 4 种状态:

  • 无锁,不锁住资源,多个线程只有一个能修改资源成功,其他线程会重试。
  • 偏向锁,同一个线程获取同步资源时,没有别人竞争时,去掉所有同步操作,相当于没锁。
  • 轻量级锁,多个线程抢夺同步资源时,没有获得锁的线程使用 CAS 自旋等待锁的释放。
  • 重量级锁,多个线程抢夺同步资源时,使用操作系统的互斥量进行同步,没有获得锁的线程阻塞等待唤醒。
    锁的升级过程:无锁-》偏向锁-》轻量级锁-》重量级锁。注意,升级并不一定是一级级升的,有可能跨级别,比如由无锁状态,直接升级为轻量级锁。

五、总结

synchronized 在 jdk 1.6 版本进行了优化,性能有了巨大提升,基本上和 java 锁性能没有什么差异,所以在生产环境中,synchronized 能满足的场景,尽量使用 synchronized,简单方便。

优化的关键,是使用了轻量级锁,使用 CAS,还有自适应机制,避免了向底层操作系统申请互斥量,避免了用户态和内核态的切换,也就是在一定程度上避免了线程上下文的切换,暂时不进入重量级锁的状态。

synchronized关键字是并发编程不可或缺的部分,Java中每一个对象都可以作为锁,这是synchronized实现同步的基础:

普通同步方法(实例方法),锁是当前实例对象 ,进入同步代码前要获得当前实例的锁
静态同步方法,锁是当前类的class对象 ,进入同步代码前要获得当前类对象的锁
同步方法块,锁是括号里面的对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。
Synchronized是Java中解决并发问题的一种最常用最简单的方法 ,他可以确保线程互斥的访问同步代码

你可能感兴趣的:(java)