synchronized这个关键字相信大家都不陌生,在多线程并发的情况下使用它来保障共享资源的安全应该是最简单的方式。但是为了高效不应该因为其使用简单而放肆的滥用它,我们应该知其然而知其所以然,这样才能更好的高效开发并且提升自己的知识深度。
那我废话不多说了,咱们开始吧!
一、简单概述
synchronized是Java为多线程并发情况提供的一种保障共享资源安全的一个关键字,它需要作用于对象上。
1.1 synchronized特性
1.1.1 原子性
原子性是指一个操作或者一组操作,要么全部执行且执行过程不会被任何因素打断,要么就都不执行。
synchronized 对临界资源加锁时,保证了临界资源对于线程间的原子性。比如高清无码资源被线程A拿到,并且因为资源非常好所以线程 A 多次进入资源进行使用(可重入),但是此时线程 B 也想来观摩观摩,但不好意思资源已被 A 占用,即使 A 退出资源一次但还没有全部退出时,线程 B 只能甘等。
1.1.2 有序性
有序性值程序执行的顺序按照代码先后执行
Java允许编译器和处理器对指令进行重排,但是指令重排并不会影响单线程的顺序,它影响的是多线程并发执行的顺序性。synchronized保证了每个时刻都只有一个线程访问同步代码块,也就确定了线程执行同步代码块是分先后顺序的,保证了有序性。
1.1.3 可见性
可见性是指多个线程访问一个资源时,该资源的状态、值信息等对于其他线程都是可见的
synchronized对一个类或对象加锁时,一个线程如果要访问该类或对象必须先获得它的锁,而这个锁的状态对于其他任何线程都是可见的,并且在释放锁之前会将对变量的修改刷新到主存当中,保证资源变量的可见性,如果某个线程占用了该锁,其他线程就必须在锁池中等待锁的释放。
1.1.4 重入性
当一个线程试图操作一个由其他线程持有的对象锁的临界资源时,将会处于阻塞状态,但当一个线程再次请求自己持有对象锁的临界资源时,这种情况属于重入锁。通俗一点讲就是说一个线程拥有了锁仍然还可以重复申请锁。
1.2 用法
synchronized 一共有三种使用方法并且锁的对象各不相同:
1.2.1 直接修饰实例方法
在这种情况下多线程并发访问实例方法时,如果其他线程调用同一个对象的被 synchronized 修饰的方法,就会被阻塞。
相当于把锁记录在这个方法对应的实例对象上。
public synchronized void set(){
i++;
}
复制代码
1.2.2 直接修饰静态方法
在这种情况下进行多线程并发访问时,如果其他线程也是调用属于同一类的被 synchronized 修饰的静态方法,就会被阻塞。
相当于把锁信息记录在这个方法对应的类上。
public synchronized static void set(){
i++;
}
复制代码
1.2.3 直接修饰代码块
如果此时有别的线程也想访问某个被 synchronized(对象A) 修饰的同步代码块时,也会被阻塞。
同步代码块会把锁记录在对象A上
public void set(){
synchronized(对象A){
i++
}
}
复制代码
synchronized的三种锁示意图:

二、原理实现
在理解锁实现原理之前先了解一下Java的对象头和Monitor
2.1 对象头
首先我们来看看对象的内存布局

内存中的对象一般由三部分组成,分别是对象头、对象实际数据和对齐填充。大家可以看看我的这篇博客详细了解👉《链接》
对象头包含 Mark Word(对象头)、Class Pointer(类型指针)和 Length(数组长度) 三部分。
- Mark Word:记录了对象关于锁的信息,垃圾回收信息等。
- Class Pointer:用于指向对象对应的 Class 对象(其对应的元数据对象)的内存地址。
- Length:只适用于对象是数组时,它保存了该数组的长度信息。
我们刚才讲的锁 synchronized 锁使用的就是对象头的 Mark Word 字段中的一部分,Mark Word 中的某些字段发生变化,就可以代表锁不同的状态。
由于锁的信息是记录在对象里的,有的开发者也往往会说锁住对象这种表述。
在32位和64位操作系统上 Mark Word 的结构图

本文详细介绍了Java中的synchronized关键字,包括其特性(原子性、有序性、可见性和重入性),使用方式(修饰实例方法、静态方法和代码块)以及原理实现。重点讲解了锁的膨胀升级过程,从无锁、偏向锁、轻量级锁到重量级锁的转变,分析了各种锁的状态和转换条件,旨在帮助读者理解synchronized的高效使用和优化机制。
最低0.47元/天 解锁文章
1万+

被折叠的 条评论
为什么被折叠?



