IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 多线程与高并发(3)——synchronized原理 -> 正文阅读

[Java知识库]多线程与高并发(3)——synchronized原理

synchronized用法参考上一篇文章,多线程与高并发(2)——synchronized用法详解,里面也详细讲了原子性、可见性和互斥性。本文主要总结synchronized的原理。
synchronized的同步块对同一条线程来说是可重入的,不会出现自己把自己锁死的问题。

一、反编译

我们先看以下代码:

 // 对象锁:形式1(方法锁)
    public synchronized void method1() {
        System.out.println("我是对象锁也是方法锁");
        try {
            System.out.println("我要睡500ms");
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    // 对象锁:形式2(代码块形式)
    public void method2() {
        synchronized (this) {
            try {
                System.out.println("我是对象锁,我要睡500ms");
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    // 类锁:形式1 :锁静态方法
    public static synchronized void method3() {
        System.out.println("我是类锁1");
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

	public void method4() {
        synchronized (DemoSync.class) {
            System.out.println("我是类锁种类2");
        }

这是在上一篇文章中总结的synchronized用法,方法12锁的是对象,方法3锁的是整个类(或者说这个类的Class对象)。这里我们用javap -v命令进行class文件的反编译,结果分别如下。

1、method1
在这里插入图片描述
2、method2
在这里插入图片描述
3、method3
在这里插入图片描述
4、method4
在这里插入图片描述

二、monitorenter和monitorexit

1、monitorenter
源码中这么解释:

Each object is associated with a monitor. A monitor is locked if and only if it has an 
owner. The thread that executes monitorenter attempts to gain ownership of the 
monitor associated with objectref, as follows:
? If the entry count of the monitor associated with objectref is zero, the thread 
enters the monitor and sets its entry count to one. The thread is then the owner of 
the monitor.
? If the thread already owns the monitor associated with objectref, it reenters the 
monitor, incrementing its entry count.
? If another thread already owns the monitor associated with objectref, the thread 
blocks until the monitor's entry count is zero, then tries again to gain ownership.

翻译过来就是:
每个对象有一个监视器(monitor),或者说叫管程。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下:
(1)如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者。
(2)如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1。
(3)如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权。
2、monitorexit

The thread that executes monitorexit must be the owner of the monitor associated with 
the instance referenced by objectref.
The thread decrements the entry count of the monitor associated with objectref. If as 
a result the value of the entry count is zero, the thread exits the monitor and is no 
longer its owner. Other threads that are blocking to enter the monitor are allowed to
 attempt to do so.

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

Synchronized的语义底层是通过一个monitor的对象来完成,其实wait/notify等方法也依赖于monitor对象,这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法,否则会抛出java.lang.IllegalMonitorStateException的异常的原因。
可以看我们多线程系列的第一篇文章,多线程与高并发(1)——线程的基本知识(实现,常用方法,状态),里面有讲到wait()方法必须在同步关键字修饰的方法中才能调用。调用notify或者notifyAll方法并不释放锁,而是让他参与锁的竞争中去。

三、ACC_SYNCHRONIZED

方法1和3的同步并没有通过指令 monitorenter 和 monitorexit 来完成(理论上其实也可以通过这两条指令来实现),不过相对于普通方法,其常量池中多了 ACC_SYNCHRONIZED,ACC_STATIC 标示符。JVM就是根据ACC_SYNCHRONIZED来实现方法的同步的:

当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,
执行线程将先获取monitor,获取成功之后才能执行方法体,方法执行完后(无论是正常或者非正常)
再释放monitor。在方法执行期间,其他任何线程都无法再获得同一个monitor对象。

如果一个同步方法执行期间抛出了异常,并且在方法内部无法处理此异常,那么这个同步方法所持有的管程将在异常抛到同步方法之外时自动释放。
两种同步方式本质上没有区别,只是方法的同步是一种隐式的方式来实现,无需通过字节码来完成。两个指令的执行是JVM通过调用操作系统的互斥原语mutex来实现,被阻塞的线程会被挂起、等待重新调度,会导致“用户态和内核态”两个态之间来回切换,对性能有较大影响。

四、总结

对于method1234来说,本质上都是对象去竞争monitor,谁争到了谁就去执行。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-04-23 10:43:02  更:2022-04-23 10:45:05 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 4:44:55-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码