博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
synchronized的原理及其使用说明
阅读量:2724 次
发布时间:2019-05-13

本文共 7384 字,大约阅读时间需要 24 分钟。

首先synchronized是阻塞式锁,悲观锁,会造成线程的挂起和恢复操作。JVM JDK1.2以后的线程模型是用户态和内核态共同存在,存在切换开销。

 

先看段代码

public class Test2 {    public synchronized void test() {        System.out.println("hello");    }    public void test3() {        synchronized (this) {            System.out.println("hello");        }    }    public static synchronized void test2() {        System.out.println("hello");    }}

常用用法,修饰对象,方法,静态方法

看原理

使用javap -c xx.class > 1.txt

public synchronized void test();    Code:       0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;       3: ldc           #3                  // String hello       5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V       8: return  public void test3();    Code:       0: aload_0       1: dup       2: astore_1       3: monitorenter       4: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;       7: ldc           #3                  // String hello       9: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V      12: aload_1      13: monitorexit      14: goto          22      17: astore_2      18: aload_1      19: monitorexit      20: aload_2      21: athrow      22: return    Exception table:       from    to  target type           4    14    17   any          17    20    17   any  public static synchronized void test2();    Code:       0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;       3: ldc           #3                  // String hello       5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V       8: return

通过javap -v即可显示详情

Classfile /Users/huahua/IdeaProjects/web-filter/target/classes/com/feng/demo/Test.class  Last modified 2020-5-6; size 731 bytes  MD5 checksum caa59f5b81fc01bdb2e60cd740a60c5a  Compiled from "Test.java"public class com.feng.demo.Test  minor version: 0  major version: 52  flags: ACC_PUBLIC, ACC_SUPERConstant pool:   #1 = Methodref          #6.#23         // java/lang/Object."
":()V #2 = Fieldref #24.#25 // java/lang/System.out:Ljava/io/PrintStream; #3 = String #26 // hello #4 = Methodref #27.#28 // java/io/PrintStream.println:(Ljava/lang/String;)V #5 = Class #29 // com/feng/demo/Test #6 = Class #30 // java/lang/Object #7 = Utf8
#8 = Utf8 ()V #9 = Utf8 Code #10 = Utf8 LineNumberTable #11 = Utf8 LocalVariableTable #12 = Utf8 this #13 = Utf8 Lcom/feng/demo/Test; #14 = Utf8 test #15 = Utf8 test3 #16 = Utf8 StackMapTable #17 = Class #29 // com/feng/demo/Test #18 = Class #30 // java/lang/Object #19 = Class #31 // java/lang/Throwable #20 = Utf8 test2 #21 = Utf8 SourceFile #22 = Utf8 Test.java #23 = NameAndType #7:#8 // "
":()V #24 = Class #32 // java/lang/System #25 = NameAndType #33:#34 // out:Ljava/io/PrintStream; #26 = Utf8 hello #27 = Class #35 // java/io/PrintStream #28 = NameAndType #36:#37 // println:(Ljava/lang/String;)V #29 = Utf8 com/feng/demo/Test #30 = Utf8 java/lang/Object #31 = Utf8 java/lang/Throwable #32 = Utf8 java/lang/System #33 = Utf8 out #34 = Utf8 Ljava/io/PrintStream; #35 = Utf8 java/io/PrintStream #36 = Utf8 println #37 = Utf8 (Ljava/lang/String;)V{ public com.feng.demo.Test(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."
":()V 4: return LineNumberTable: line 3: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcom/feng/demo/Test; public synchronized void test(); descriptor: ()V flags: ACC_PUBLIC, ACC_SYNCHRONIZED Code: stack=2, locals=1, args_size=1 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #3 // String hello 5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return LineNumberTable: line 5: 0 line 6: 8 LocalVariableTable: Start Length Slot Name Signature 0 9 0 this Lcom/feng/demo/Test; public void test3(); descriptor: ()V flags: ACC_PUBLIC Code: stack=2, locals=3, args_size=1 0: aload_0 1: dup 2: astore_1 3: monitorenter 4: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 7: ldc #3 // String hello 9: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 12: aload_1 13: monitorexit 14: goto 22 17: astore_2 18: aload_1 19: monitorexit 20: aload_2 21: athrow 22: return Exception table: from to target type 4 14 17 any 17 20 17 any LineNumberTable: line 9: 0 line 10: 4 line 11: 12 line 12: 22 LocalVariableTable: Start Length Slot Name Signature 0 23 0 this Lcom/feng/demo/Test; StackMapTable: number_of_entries = 2 frame_type = 255 /* full_frame */ offset_delta = 17 locals = [ class com/feng/demo/Test, class java/lang/Object ] stack = [ class java/lang/Throwable ] frame_type = 250 /* chop */ offset_delta = 4 public static synchronized void test2(); 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: ldc #3 // String hello 5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return LineNumberTable: line 15: 0 line 16: 8}SourceFile: "Test.java"

对应上面的代码,class字节码,比如aload_1即class虚拟机字节码指令,其实很多语言可以编译为class文件,所有Java虚拟机实际上是class虚拟机。

可以看出本质monitorenter和monitorexit指令

同步方法其实是

flags: ACC_SYNCHRONIZED,通过-v参数可以直观地看见底层实现。

实际上,锁的本质满足如下:

1、原子性:当一个线程操作共享变量的时候,其他线程不能操作。实际就是串行修改。

2、可见性:当一个线程修改一个变量的时候,其他的线程及时知道更新缓存变量(参考计算机原理CPU与内存或主存的交互设计)

3、(可选)可重入:当一个线程拿到某个锁后,当该线程在释放该锁之前,可以重复获取该锁,当然释放次数与获取次数相同,否则死锁

从上可看出synchronized 的用途

1. 对于同步方法,锁是当前实例对象。

2. 对于同步方法块,锁是Synchonized括号里的对象。

3. 对于静态同步方法,锁是当前对象的Class对象。类似 synchronized (this.class)

JDK8及以后的版本JDK对synchronized做了优化,对于写操作频繁的线程安全推荐使用,性能较强

synchronized是操作对象头的monitor实现的,所以当时用sleep和wait的时候,在sleep方法中没有释放monitor,所以锁未释放,而wait方法释放了monitor对象,释放了锁

核心原理:JMM模型操作协议原子性

monitorenter和monitorexit指令操作lock和unlock指令, 保证主内存与工作内存的一致性

你可能感兴趣的文章
QT+FFMPEG4.0 Windows开发环境搭建
查看>>
值得推荐的C/C++框架和库
查看>>
讲课常用词词频分析
查看>>
FFMPEG进阶系列01-ffplay命令详解
查看>>
FFMPEG进阶系列02-ffmpeg命令详解1
查看>>
FFMPEG进阶系列02-ffmpeg命令详解2
查看>>
FFMPEG进阶系列02-ffmpeg命令详解3
查看>>
FFMPEG进阶系列03-ffmpeg转码专题(上)
查看>>
FFMPEG进阶系列03-ffmpeg转码专题(中)x264参数详解
查看>>
在线技术文章总结整理
查看>>
Ubuntu 18.04安装ROS Melodic
查看>>
一线互联网公司内推
查看>>
Linux高性能网络:协程系列01-前言
查看>>
Linux高性能网络:协程系列02-协程的起源
查看>>
Linux高性能网络:协程系列03-协程的案例
查看>>
Linux高性能网络:协程系列04-协程实现之工作原理
查看>>
Linux高性能网络:协程系列05-协程实现之原语操作
查看>>
Linux高性能网络:协程系列06-协程实现之切换
查看>>
Linux高性能网络:协程系列07-协程实现之定义
查看>>
Linux高性能网络:协程系列08-协程实现之调度器
查看>>