synchronized 关键字-监视器锁 monitor lock

news2024/10/6 10:28:36

1.代码示例:

package thread3;

import java.util.Scanner;

public class Test2 {
    public static Object object = new Object();

    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(() -> {
            Scanner scanner = new Scanner(System.in);
            while (true) {
                synchronized (object) {
                    System.out.println("请输入一个整数:");
                    int a = scanner.nextInt();
                    System.out.println("a: " + a);
                }
            }
        });
        thread1.start();

        Thread.sleep(2000);

        Thread thread2 = new Thread(() -> {
            synchronized (object) {
                System.out.println("hello thread2");
            }
        });
        thread2.start();
    }
}

 

 


 

 加锁的时候,如果针对不同的对象加锁,这就意味着俩个线程之间不会有任何的锁竞争.

2.synchronized 的特性

互斥

synchronized 会起到互斥效果, 某个线程执行到某个对象的 synchronized 中时, 其他线程如果也执行到同一个对象 synchronized 就会阻塞等待.

  • 进入 synchronized 修饰的代码块, 相当于 加锁
  • 退出 synchronized 修饰的代码块, 相当于 解锁
     

可以粗略理解成, 每个对象在内存中存储的时候, 都存有一块内存表示当前的 "锁定" 状态(类似于厕所的 "有人/无人").
如果当前是 "无人" 状态, 那么就可以使用, 使用时需要设为 "有人" 状态.
如果当前是 "有人" 状态, 那么其他人无法使用, 只能排队 

 

一个线程先上了锁,其他线程只能等待这个线程释放! 

理解 "阻塞等待"
针对每一把锁, 操作系统内部都维护了一个等待队列. 当这个锁被某个线程占有的时候, 其他线程尝
试进行加锁, 就加不上了, 就会阻塞等待, 一直等到之前的线程解锁之后, 由操作系统唤醒一个新的
线程, 再来获取到这个锁.

注意:
上一个线程解锁之后, 下一个线程并不是立即就能获取到锁. 而是要靠操作系统来 "唤醒". 这
也就是操作系统线程调度的一部分工作.
假设有 A B C 三个线程, 线程 A 先获取到锁, 然后 B 尝试获取锁, 然后 C 再尝试获取锁, 此时 B
和 C 都在阻塞队列中排队等待. 但是当 A 释放锁之后, 虽然 B 比 C 先来的, 但是 B 不一定就能
获取到锁, 而是和 C 重新竞争, 并不遵守先来后到的规则.
 

synchronized的底层是使用操作系统的mutex lock实现的.
 

刷新内存

synchronized 的工作过程:

  • 获得互斥锁
  • 从主内存拷贝变量的最新副本到工作的内存
  • 执行代码
  • 将更改后的共享变量的值刷新到主内存
  • 释放互斥锁

 所以 synchronized 也能保证内存可见性. 具体代码参见后面 volatile 部分.

可重入

synchronized 同步块对同一条线程来说是可重入的,不会出现自己把自己锁死的问题

package thread1;

class Counter {
    public int count = 0;

    synchronized public void increase() {
        synchronized (this) {
            count++;
        }
    }
}

public class Test10 {
    private static Counter counter = new Counter();

    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 50000; i++) {
                counter.increase();
            }
        }, "thread1");

        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 50000; i++) {
                counter.increase();
            }
        }, "thread2");

        thread1.start();
        thread2.start();

        thread1.join();
        thread2.join();

        System.out.println(counter.count);
    }
}

1.当调用increase的时候,先进行加锁操作,针对this来加锁(此时this就是一个锁定的状态了,把this对象头中的标志位给设置上了)

2.继续执行到下面的代码块的时候,也会尝试再次加锁,由于此时this已经是锁定状态了,按照之前的理解,这里的加锁操作就会出现阻塞,这里的阻塞啥时候结束呢?等到之前的代码把锁给释放了。要执行完这个方法,锁才能释放,但是由于此处的阻塞,导致当前这个方法没法继续执行了(僵住了)。

连续针对一个同一个对象(此处是this,也就是counter对象),加锁俩次,并且如果锁不是可重入的话,就会出现死锁。
 

Java设计锁的大佬就考虑到了这个情况~于是就把当前这里的synchronized设计成可重入锁了.针对同一个锁,连续加锁多次,不会有负面效果~~

锁中持有了两个信息:

  • 1.当前这个锁被哪个线程给持有了.
  • 2.当前这个锁被加锁几次了~~

当线程t已经加锁成功之后
后续再次尝试加锁,就会自动的判定出,当前这把锁就是t持有的.第二次加锁不会真的"加锁",而只是进行一个修改计数.( 1 ->2)后续往下执行的时候,出了synchronized 代码块,就触发一次解锁.(也不是真的解锁,而是计数-1)在外层方法执行完了之后,再次解锁,再次计数-1,计数减成0了,才真正的进行解锁~ 

死锁出现的情况,不仅仅是上述这一种情况,(针对同一把锁,连续加俩次)

1.一个线程,一把锁

2.俩个线程,俩把锁

3.N个线程,M把锁

哲学家就餐问题

这里会单独出一篇博客,实现一下哲学家就餐问题,先介绍一下这个问题~

 

该问题描述的是五个哲学家共用一张圆桌,分别坐在周围的五张椅子上,在圆桌上有五个碗和五只筷子,他们的生活方式是交替的进行思考和进餐。平时,一个哲学家进行思考,饥饿时便试图取用其左右最靠近他的筷子,只有在他拿到两只筷子时才能进餐。进餐完毕,放下筷子继续思考。

3.Java 标准库中的线程安全类

Java 标准库中很多都是线程不安全的. 这些类可能会涉及到多线程修改共享数据, 又没有任何加锁措施.

  • ArrayList
  • LinkedList
  • HashMap
  • TreeMap
  • HashSet
  • TreeSet
  • StringBuilder

但是还有一些是线程安全的. 使用了一些锁机制来控制.

  • Vector (不推荐使用)
  • HashTable (不推荐使用)
  • ConcurrentHashMap
  • StringBuffer

还有的虽然没有加锁, 但是不涉及 "修改", 仍然是线程安全的

  • String


 

 


 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/333630.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

盂县基本情况

寒假的活动报告&#xff0c;万物皆可CSDN&#xff0c;贴一下吧 盂县隶属于阳泉市&#xff0c;阳泉市是李彦宏和刘慈欣的家乡&#xff0c;阳泉市内有百度云计算中心 基本情况 盂县&#xff0c;隶属山西省阳泉市&#xff0c;地处山西省东部、太行山西麓&#xff0c;东与河北省平…

测试开发面经

操作系统 进程与线程 进程间通信方式 进程间的六种通信方式 管道消息队列共享内存信号量信号套接字 socket长连接和短连接 长连接与短连接的概念&#xff1a;前者是整个通讯过程&#xff0c;客户端和服务端只用一个Socket对象&#xff0c;长期保持Socket的连接&#xff1b…

Windows/VM虚拟机安装黑群晖6.1-----保证有效而且简单操作

1视频&#xff1a;Windows/VM虚拟机安装黑群晖教程_哔哩哔哩_bilibili2:网址&#xff1a;Synology Web Assistant3&#xff1a;重新打开群晖操作步骤1&#xff1a;按着视频下载好资源后&#xff0c;按照视频操作&#xff0c;途中修改地方&#xff08;两个情况选择其中一个&…

Docker系列(镜像原理)03

前言 镜像就是图中的集装箱&#xff0c;仓库就是超级码头&#xff0c;容器就是我们运行程序的地方。 从联合文件系统说起 Union文件系统(UnionFS )是一种分层、轻量级并且高性能的文件系统。它支持对文件系统的修改作为一次提交来一层层的叠加&#xff0c;同时可以将不同目录…

【游戏逆向】】游戏全屏捡物的分析实现

前言 在角色对战类中&#xff0c;拾取怪物掉落的装备是一项必备的工作&#xff0c;由于装备位置掉落的不确定性&#xff0c;玩家想要拾取离角色距离较远的装备需要一定的时间&#xff0c;这一段时间往往会影响游戏的评分或是玩家的心态&#xff0c;基于此&#xff0c;全屏捡物…

【Unity VR开发】结合VRTK4.0:键盘操作运动与旋转

前言&#xff1a; 当我们的手柄无发进行VR测试&#xff0c;或者想通过键盘进行验证&#xff0c;那么就用到了我们今天的一个功能&#xff1a;组合操作。组合操作允许更复杂的输入类型&#xff0c;我们将介绍如何使用布尔输入&#xff08;如键盘键&#xff09;来模拟模拟轴&…

Java中LinkedList增删改比ArrayList快吗?

在 Java 中&#xff0c;LinkedList 和 ArrayList 的性能是不同的&#xff0c;具体取决于你所需要的操作。 对于频繁的插入和删除操作&#xff0c;LinkedList 的性能通常更好&#xff0c;因为它使用了链表数据结构&#xff0c;只需更改节点的指针就可以在链表中插入或删除元素。…

剑指Offer 第26天 表示数值的字符串 Hard

表示数值的字符串_牛客题霸_牛客网 描述 请实现一个函数用来判断字符串str是否表示数值&#xff08;包括科学计数法的数字&#xff0c;小数和整数&#xff09;。 科学计数法的数字(按顺序&#xff09;可以分成以下几个部分: 1.若干空格 2.一个整数或者小数 3.&#xff08;可选&…

计算机组成原理第七章笔记记录

仅仅作为笔记记录,B站视频链接&#xff0c;若有错误请指出&#xff0c;谢谢 基本概念 演变过程 I/O系统基本组成 I/O软件 包括驱动程序、用户程序、管理程序、升级补丁等 下面的两种方式是用来实现CPU和I/O设备的信息交换的 I/O指令 CPU指令的一部分,由操作码,命令码,设备…

前端 基于 vue-simple-uploader 实现大文件断点续传和分片上传

文章目录一、前言二、后端部分新建Maven 项目后端pom.xml配置文件 application.ymlHttpStatus.javaAjaxResult.javaCommonConstant.javaWebConfig.javaCheckChunkVO.javaBackChunk.javaBackFileList.javaBackChunkMapper.javaBackFileListMapper.javaBackFileListMapper.xmlBac…

2023年rabbitMq面试题汇总4(7道)

一、如何保证消息的顺序性&#xff1f;1. 通过某种算法&#xff0c;将需要保持先后顺序的消息放到同⼀个消息队列中(kafka中就是partition,rabbitMq中就是queue)。然后只⽤⼀个消费者去消费该队列。2. 可以在消息体内添加全局有序标识来实现。二、使⽤RabbitMQ增加rest服务吞吐…

JVM调优几款好用的内存分析工具

对于高并发访问量的电商、物联网、金融、社交等系统来说&#xff0c;JVM内存优化是非常有必要的&#xff0c;可以提高系统的吞吐量和性能。通常调优的首选方式是减少FGC次数或者FGC时间&#xff0c;以避免系统过多地暂停。FGC达到理想值后&#xff0c;比如一天或者两天触发一次…

【Android -- 每日一问】现在 Android 怎么学?学什么?

不管在任何行业&#xff0c;任何岗位&#xff0c;初级技术人才总是供大于求&#xff1b;不管任何行业、岗位&#xff0c;技术过硬的也都是非常吃香的&#xff01; 这几年 Android 新技术的迭代明显加速了&#xff0c;有来自外部跨平台新物种的冲击&#xff0c;有去 Java 化的商…

Dropout的原理与细节?

1. 什么是Dropout? Dropout是通过使得每个神经元以一定的概率停止工作来接缓解过拟合问题。dropout(随机失活):dropout是通过遍历神经网络每一层的节点,然后通过对该层的神经网络设置一个keep_prob(节点保留概率),即该层的节点有keep_prob的概率被保留,keep_prob的取值范围…

YOLO-V1~V3经典物体检测算法介绍

大名鼎鼎的YOLO物体检测算法如今已经出现了V8版本&#xff0c;我们先来了解一下它前几代版本都做了什么吧。本篇文章介绍v1-v3&#xff0c;后续会继续更新。一、节深度学习经典检测方法概述1.1 检测任务中阶段的意义我们所学的深度学习经典检测方法 &#xff0c;有些是单阶段的…

windows无法访问指定设备路径或文件怎么办?2个解决方案

有时候Win10电脑打不开程序或文件&#xff0c;windows无法访问指定设备路径或文件该怎么办&#xff1f;原因是什么呢&#xff1f;一般导致这种情况的出现&#xff0c;大多是因为我们的电脑缺乏相应的查看权限&#xff0c;我们只需要通过赋予权限就可以解决这个难题了。 操作环境…

负载均衡下的webshell上传

负载均衡下的webshell上传1.应用场景2.面临的困难2.1 shell文件上传问题2.2 命令执行时的漂移2.3 大工具投放失败2.4 内网穿透工具失效3.一些解决方案3.1 关机3.2 基于IP判断执行主机3.3 脚本实现web层的流量转发3.3.1 创建antproxy.jsp脚本3.3.2 修改 Shell 配置4.总结1.应用场…

零基础学SQL(十、子查询与多表关联)

目录 前置建表 ​编辑 编辑 一、子查询 1、什么是子查询 2、子查询的类型 二、表关联查询 1、连接分类 1.1、交叉连接 &#xff08;CROSS JOIN&#xff09; 1.2、内连接&#xff08;inner join&#xff09; 1.3、外连接&#xff08;left join ,right join,full joi…

Nodejs原型链污染

Nodejs与JavaScript和JSON 有一些人在学习JavaScript时会分不清Nodejs和JavaScript之间的区别&#xff0c;如果没有node&#xff0c;那么我们的JavaScript代码则由浏览器中的JavaScript解析器进行解析。几乎所有的浏览器都配备了JavaScript的解析功能&#xff08;最出名的就是…

《Terraform 101 从入门到实践》 第二章 Providers插件管理

《Terraform 101 从入门到实践》这本小册在南瓜慢说官方网站和GitHub两个地方同步更新&#xff0c;书中的示例代码也是放在GitHub上&#xff0c;方便大家参考查看。 不怕出身低&#xff0c;行行出状元。 插件 Terraform可以对多种平台的多种资源进行管理&#xff0c;这个是通过…