是一种用于实现线程间同步的Java关键字,主要目的是确保在多线程环境下,对共享资源的访问是安全的,避免出现数据不一致的问题。就是保证在同一时刻,被synchronized修饰的方法或代码块只有一个线程在执行,其他线程必须等待,解决并发安全问题
参考
https://www.cnblogs.com/zys2019/p/17265104.html
https://zhuanlan.zhihu.com/p/410056254?utm_medium=social&utm_oi=733685299476992000
https://www.cnblogs.com/semi-sub/p/12906660.html
https://juejin.cn/post/7046921350065160206
synchronized的锁可分为类锁和对象锁。
1)类锁
是用来锁类的,让所有对象共同争抢一把锁。一个类的所有对象共享一个class对象,共享一组静态方法,类锁的作用就是使持有者可以同步地调用静态方法。当synchronized修饰静态方法或者class对象的时候,拿到的就是类锁。
2)对象锁
是用来锁对象的,虚拟机为每个的非静态方法和非静态域都分配了自己的空间,每个对象各有一把锁,即同一个类如果有两个对象就有两把锁。synchronized修饰非静态方法或者this时拿到的就是对象锁。
synchronized放在类方法上,也就是我们所说的静态方法上,锁对象是方法区中的类对象,是一个全局锁。
当对静态方法加锁后,就使用了类锁,这个类的所有对象共享这个静态方法,这个方法就是同步方法。换句话说,就是当一个线程执行一个对象中的同步方法时,其他线程调用同一对象上的同步方法将会被阻塞,直到第一个线程完成使用这个对象。
public class AddUtil {
private static int i = 0; //共享资
public synchronized static void synBlock() {
for (int j = 0; j < 10000; j++) {
i++;
}
System.out.println(i);
}
}
synchronized放在实例方法上,锁对象是当前的this对象
public class fancySyncTest {
public synchronized void method1(){
try {
System.out.println(Thread.currentThread().getName());
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] agrs) throws InterruptedException {
final fancySyncTest fs = new fancySyncTest();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
fs.method1();
}
},"线程1获取到资源");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
fs.method1();
}
},"线程2获取到资源");
t1.start();
t2.start();
}
}
synchronized修饰代码块,也就是synchronized(object){},锁对象是()中的对象
public class TestNotes {
private static Object object;
public String decStock() {
synchronized (object) {
//todo
}
return "OK";
}
}
针对synchronized修饰的地方不同,实现的原理不同
是基于 Java 对象头中的监视器锁(Monitor)实现的;最终依赖操作系统的Mutex lock(互斥锁)来实现的。
每个Java对象都可以用作一个实现同步的锁,这种锁被称为内置锁或对象锁或者Monitor锁。当一个线程进入一个synchronized方法或代码块时,它会尝试获取该对象的锁;如果锁已被其他线程持有,则该线程将阻塞,直到锁被释放。一旦线程获得了锁,它就可以执行synchronized代码块中的操作,并在退出代码块时释放锁(无论是正常路径退出,还是通过抛出异常退出)。
public class SynchronizedDemo {
private static int i = 0; //共享资
public void synBlock() {
synchronized (this) {
for (int j = 0; j < 10000; j++) {
i++;
}
}
}
}
使用jclasslib查看字节码文件,可以看出,synchronized 代码块实际上多了 monitorenter 和 monitorexit 指令。
0: aload_0
1: dup
2: astore_1
3: monitorenter
4: iconst_0
5: istore_2
6: iload_2
7: sipush 10000
10: if_icmpge 27
13: getstatic #2 // Field i:I
16: iconst_1
17: iadd
18: putstatic #2 // Field i:I
21: iinc 2, 1
24: goto 6
27: aload_1
28: monitorexit
29: goto 37
32: astore_3
33: aload_1
34: monitorexit
35: aload_3
36: athrow
37: return
对于同步代码块来说,主要使用monitor(监视器)实现的,就相当于一个房间一次只能被允许一个进程进入。主要包含monitorenter和monitorexit。其中monitorenter指向同步代码块开始的位置,monitorexit指向同步代码块结束的位置。当执行monitorenter时,线程试图monitor的持有权,当monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者。若其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权。当执行monitorexit时,monitor的进入数减1,如果减1后进入数为0,那线程退出monitor。
对于同步方法来说,使用的是ACC_SYNCHRONIZED 标识,通过该标识指明该方法是否是一个同步方法
被 synchronized 修饰的方法会有一个 ACC_SYNCHRONIZED 标志。当某个线程要访问某个方法的时候,会首先检查方法是否有 ACC_SYNCHRONIZED 标志,如果有则需要先获得 monitor 锁,然后才能开始执行方法,方法执行之后再释放 monitor 锁。其他方面, synchronized 方法和刚才的 synchronized 代码块是很类似的,例如这时如果其他线程来请求执行方法,也会因为无法获得 monitor 锁而被阻塞。
修饰实例方法和修饰类方法都是ACC_SYNCHRONIZED标识符去实现的。只是它们锁住的对象不同
public static synchronized void synBlock() {
System.out.println(100);
}
运行 javap -v SynchronizedDemo.class
{
public org.java.basic.concurret.basic.SynchronizedDemo();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lorg/java/basic/concurret/basic/SynchronizedDemo;
public static synchronized void synBlock();
descriptor: ()V
flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED
Code:
stack=2, locals=0, args_size=0
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: bipush 100
5: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
8: return
LineNumberTable:
line 8: 0
line 10: 8
}
提到的monitor监视器对象存在于Java对象的对象头Mark Word中
java对象结构:
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。