synchronized关键字-监视器锁(monitor lock)

news2024/11/25 10:48:41

这就是我们上一篇中代码提到的加锁的主要方式,本质上是调用系统api进行加锁,系统api本质是靠cpu特定指令加锁.

synchronize的特性

互斥性

synchronized会起到互斥效果,某个线程执行到某个对象的synchronized中时,,其它线程如果也执行到同一个对象synchronized就会阻塞等待(锁冲突/锁竞争)

进入synchronized修饰的代码块,相当于加锁.

退出synchronized修饰的代码块,相当于解锁.

让我们回顾一下上一篇中这一段代码:

synchronized (locker) {//locker是锁对象,后面会讲
    count++;
}

进入代码块内部(第一个大括号),相当于针对当前对象加锁.

执行完毕(出第二个大括号)相当于针对当前对象"解锁" .

让我们在多线程的场景下分析一下这个过程.

通过锁竞争可以让第二个线程指令无法插入到第一个线程指令中间,但此时第一个线程仍可被调度cpu. 

上述过程就可以看作不同的人(线程)排队上厕所,一个人进去就得上锁,这时其它人进不去,直到那个人开了锁才可以.

理解"阻塞等待"

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

注意:

上一个线程解锁之后,下一个线程并不是就能够立即获取到锁.而是要靠操作系统来"唤醒".这也就是操作系统线程调度的一部分工作.

假设有A B C三个线程,线程A先获取到锁,然后B尝试获得锁,然后C尝试获得锁,此时B和C都在阻塞队列中排队等待.但是A释放锁之后,虽然B比C先来的,但是B不一定能获取到锁,而是和C重新竞争,并不遵守先来后到的规则.

利用锁确实对多线程执行效率有影响,但这样仍会比串行执行快,因为锁以外的的内容仍然是并发执行的

可重入

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

因此Java不会出现锁死问题,但锁死的内容仍需要了解

理解"把自己锁死"

一个线程没有释放锁,然后又尝试加锁.

//第一次加锁,加锁成功

lock();

//第二次加锁,锁已经被占用,阻塞等待.

lock();

按照之前锁的设定,第二次加锁的时候,就会阻塞等待.直到第一次的锁被释放,才能获取到第二个锁.但是释放第一个锁也是由该线程来完成,结果这个线程已经躺平了,啥都不想干了,就无法进行解锁操作.这时候就会死锁.

死锁的三种典型场景

1.一个线程一把锁:如果锁不是可重入锁.并且一个线程对这把锁2次就会出现死锁(把钥匙锁在屋里了).

public class TestLock {
    public static Object locker = new Object();
    public static int count = 0;
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            for(int i = 0; i < 50000; i++) {
                synchronized (locker) {
                    synchronized (locker) {
                        count++;
                    }
                }
            }
        });

        Thread t2 = new Thread(() -> {
           for(int i = 0; i < 50000; i++) {
               synchronized (locker) {
                   count++;
               }
           }
        });

        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(count);
    }
}

这里的t1按理来说是死锁的类型,不过synchronized是可重入锁,所以可以正常执行.

2.两个线程两把锁:线程1获取到锁A,线程2获取到锁B,接下来线程1尝试获取到锁B,线程2尝试获取到锁A就会导致死锁.(房子的钥匙锁车里了,车钥匙锁房子里了). 

public class TestLock2 {
    public static Object A = new Object(), B = new Object();

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
           synchronized (A) {
               //sleep一下,是给t2时间,让t2也能拿到B
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
               //尝试获取B,并没有释放A
               synchronized (B) {
                   System.out.println("t1拿到了两把锁");
               }
           }
        });

        Thread t2 = new Thread(() -> {
           synchronized (B) {
               //sleep一下,是给t1时间,让t1能拿到A
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
               //尝试获取A,并没有释放B
               synchronized (A) {
                   System.out.println("t2 拿到了两把锁");
               }
           }
        });

        t1.start();
        t2.start();
    }
}

因此,形如这样的代码不会执行到第二次获取锁内的内容,通过jconsole可以观察到原因.

 

可见,两个线程都卡在了获取对方已获取得到 的锁的地方,而且状态为BLOCKED.

不过在这种情况下,仍可以通过约定加锁顺序来解决问题.

3.n个线程可以获取到m把锁. (建议看一下哲学家吃饭问题,这里就不过多讲解了).

死锁的四个必要条件(以下的条件缺一不可,缺一个不构成死锁)

1.互斥使用:获取锁的过程是互斥的.一个线程拿到了这把锁,另一个线程也想获取,就需要阻塞等待.(锁的基本特性,不好破坏)

2.不可抢占:一个线程拿到锁之后,只能主动解锁,不能让别叠对象把锁强行抢走(锁的基本特性,也不好破坏)

3.请求保持:一个线程拿到锁A之后,在持有A的条件下,尝试获取B(代码结构,看实际需求)

4.循环等待(环路等待):一个想获取到另一个的锁,另一个又在等其它的.(是最容易破坏的:指定一定规则,可避免循环等待->比如指定加锁顺序)

解决死锁的方案

(1)引入一个额外的锁

(2)去掉一个线程

(3)引入计数器,限制同时工作的线程数

(4)前面三个方案普适性不高,还是建议这个:引入加锁规则

以下面的代码为例,让我们分析一下synchronized的可重入性.

 在可重入锁的内部,包含着"线程持有者"和"计数器"两个信息.

如果某个线程加锁的时候,发现锁已经被人占用,但是恰好占用的正是自己,那么就可以继续获取到锁,让计数器自增.(真正加锁,同时给计数器+1(初始为0,加锁之后变成1了,说明当前这个对象被该线程加锁一次),同时记录线程是谁,解锁时把count--,直到count减到零,才算是真正的解锁了)

synchronized使用实例

synchronized本质上要修改指定对象的"对象头".从使用角度来看,synchronized也一定要搭配一个具体的对象使用.

修饰代码块

明确指定锁哪个对象(比较常用的方法).

锁任意对象:

public class TestSynchronizedDemo {
    private Object locker = new Object();
    
    public void method() {
        Synchronized (locker) {
        
        }
    }
}

锁当前对象:

public class TestSynchronizedDemo {
    public void method() {
        synchronized(this) {
            //需要理解好这里是不是同一个对象
        }
    }
}

直接修饰普通方法

锁的TestSynchronizedDemo对象

public class TestSynchronizedDemo {
    public synchronized void method() {
        //相当于给this加锁(锁对象this)
    }
}

 修饰静态方法(不常见)

锁的TestSynchronizedDemo类对象(就如果synchronized是加到static方法上,相当于给类加锁).

public class TestSynchronized {
    public synchronized static void method() {
    
    }
}

 我们要重点理解,synchronized锁的是什么.两个线程竞争同一把锁,才会产生阻塞等待

两个线程分别尝试获取两把不同的锁,不会产生锁竞争.

Java标准库中的线程安全类

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

比如:ArrayList,LinkedList,HashMap,TreeMap,HashSet,TreeSet,StringBuilder

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

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

还有的是没有加锁,但因为不涉及修改的特殊类,也是线程安全的.

String

 

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

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

相关文章

快速安装Axure RP Extension for Chrome插件

打开原型文件的html&#xff0c;会跳转到这个页面&#xff0c;怎么破&#xff1f; 我们点开产品设计的原型图如果没有下载Axure插件是打不开&#xff0c;而我们国内网通常又不能再google商店搜索对应插件&#xff0c;下面教大家如何快速安装 1、打开原型文件->resources-&g…

云服务器哪家便宜?亚马逊云科技价格与性能全面解析

前言 近年来&#xff0c;随着企业数字化转型的推进&#xff0c;云服务器选择成为业界热议的话题。在众多云服务提供商中&#xff0c;亚马逊云科技备受关注&#xff0c;其云服务器在价格与性能方面的表现备受期待。为了帮助您更全面了解&#xff0c;本文将以第三方观点&#xf…

S120和PLC通讯设置

一、DP 总线通讯功能简述 PROFIBUS-DP 来实现 S7-300 与 SINAMICS S120 之间的数据交换&#xff0c;用 S7-300 来控制 S120 的运转&#xff0c;及读写所需的参数。 S7-300 与 SINAMICS S120 之间的 DP 通讯是借助于系统功能块 SFC14/SFC15 进行周期性数据通讯。 周期性数…

C# OpenCvSharp DNN 深度神经网络下的风格迁移模型

目录 介绍 效果 项目 代码 下载 C# OpenCvSharp DNN 深度神经网络下的风格迁移模型 介绍 深度神经网络下的风格迁移模型&#xff0c;适用于OpenCv、EmguCv。 斯坦福大学李飞飞团队的风格迁移模型是一种基于深度学习的图像处理技术&#xff0c;可以将一张图像的风格转移…

使用有道词典复制网页上的字

1. 今天发现一个新大陆&#xff0c;同事教的&#xff0c;有道词典可以复制网页上的字&#xff0c;也可以复制PDF文件等一些限制不可复制的字&#xff0c;原来不可复制的字&#xff0c;现在用有道都可以复制了&#xff0c;不需要用油猴下载脚本了。写给老婆这种纯电脑小白的。其…

paddleocr文字识别变迁

数据挖掘 v3 UIM&#xff1a;无标注数据挖掘方案 UIM&#xff08;Unlabeled Images Mining&#xff09;是一种非常简单的无标注数据挖掘方案。核心思想是利用高精度的文本识别大模型对无标注数据进行预测&#xff0c;获取伪标签&#xff0c;并且选择预测置信度高的样本作为训…

分布式I/O模块,为您的数据收集与控制提供强大支持

分布式I/O模块可用于数据收集和各种控制的应用。分布式I/O模块具有可靠度高、价格优惠、设置容易、网络布线方便等特性&#xff0c;适用于分散地区的应用&#xff0c;可以为您节省系统整合的时间和费用。以往都是采用控制电缆和PLC连接。如果采用了分布式I/O模块&#xff0c;就…

界面控件DevExpress WPF导航组件,助力升级应用程序用户体验!(上)

DevExpress WPF的Side Navigation&#xff08;侧边导航&#xff09;、TreeView、导航面板组件能帮助开发者在WPF项目中添加Windows样式的资源管理器栏或Outlook NavBar&#xff08;导航栏&#xff09;&#xff0c;DevExpress WPF NavBar和Accordion控件包含了许多开发人员友好的…

Win10无法删除文件需要管理员权限的解决方法

在Win10电脑中&#xff0c;用户想要删除不需要的文件&#xff0c;却收到了需要管理员权限才能删除&#xff0c;导致用户自己无法将文件删除掉。下面小编给大家带来Win10系统删除文件需要权限的解决方法&#xff0c;解决后用户在Win10电脑上就能删除任意文件了。 Win10无法删除文…

【数据结构】面试OJ题———栈|队列|互相实现|循环队列|括号匹配

目录 1. 有效的括号 思路&#xff1a; 2.用队列实现栈 思路&#xff1a; 3.用栈实现队列 思路&#xff1a; 4.设计循环队列 思路&#xff1a; 1. 有效的括号 20. 有效的括号 - 力扣&#xff08;LeetCode&#xff09; 给定一个只包括 (&#xff0c;)&#xff0c;{&…

Retrofit嵌套请求与适配器

一、前言&#xff1a; 1. retrofit嵌套请求 在实际开发中&#xff0c;可能会存在&#xff1a;需要先请求A接口&#xff0c;在请求B接口的情况&#xff0c;比如进入“玩android”网页请求获取收藏文章列表&#xff0c;但是需要先登录拿到Cookie才能请求搜藏文章几口&am…

语义分割 LR-ASPP网络学习笔记 (附代码)

论文地址&#xff1a;https://arxiv.org/abs/1905.02244 代码地址&#xff1a;https://github.com/WZMIAOMIAO/deep-learning-for-image-processing/tree/master/pytorch_segmentation/lraspp 1.是什么&#xff1f; LR-ASPP是一个轻量级语义分割网络&#xff0c;它是在Mobil…

【最新版本教程】GPT4暂停升级也可硬升!

本教程亲测整个过程&#xff0c;没有问题。 步骤&#xff1a; 1、有自己的3.5账号&#xff08;没有的可以自己去注册&#xff0c;据说现在不用手机号了&#xff0c;方法自己查去&#xff09;&#xff1b; 2、解决国外银行卡的问题。国外银行卡开通&#xff08;https://bewild…

Kafka集群调优

一、前言 我们需要对4个规格的kafka能力进行探底&#xff0c;即其可以承载的最大吞吐&#xff1b;4个规格对应的单节点的配置如下&#xff1a; 标准版&#xff1a; 2C4G铂金版&#xff1a; 4C8G专业版&#xff1a; 8C16G企业版&#xff1a; 16C32G 另外&#xff0c;一般来讲…

Excel——多列合并成一列的4种方法

Excel怎么将多列内容合并成一列&#xff1f; 怎么将多个单元格的内容连接起来放在一个单元格里&#xff1f; 比如下图&#xff0c;要将B、C、D列的内容&#xff0c;合并成E列那样&#xff0c;该怎么做呢&#xff1f; △图1 本文中&#xff0c;高潜老师将给大家介绍 4种 将多…

制作一个RISC-V的操作系统五-RISC-V汇编语言编程二

文章目录 RISC-V汇编指令操作对象RISC-V汇编指令编码格式小端序的概念RISC-V汇编指令分类RISC-V汇编伪指令 RISC-V汇编指令操作对象 RV32I&#xff1a;RISC-V32位机器整数指令集 指令集分非特权指令集和特权指令集 XLEN&#xff1a;变量代表当前机器的字长&#xff08;32位 64…

「Verilog学习笔记」根据状态转移写状态机-三段式

专栏前言 本专栏的内容主要是记录本人学习Verilog过程中的一些知识点&#xff0c;刷题网站用的是牛客网 状态机可以分为Moore状态机和Mealy状态机。 Moore状态机&#xff1a;输出只由当前状态决定Mealy状态机&#xff1a;输出由当前状态和当前的输入共同决定。 三段式状态机是指…

项目实战之RabbitMQ重试机制进行消息补偿通知

&#x1f9d1;‍&#x1f4bb;作者名称&#xff1a;DaenCode &#x1f3a4;作者简介&#xff1a;啥技术都喜欢捣鼓捣鼓&#xff0c;喜欢分享技术、经验、生活。 &#x1f60e;人生感悟&#xff1a;尝尽人生百味&#xff0c;方知世间冷暖。 文章目录 &#x1f31f;架构图&#x…

软件设计之装饰模式

装饰模式把每个要装饰的功能放在单独的类中&#xff0c;并让这个类包装它所要装饰的对象&#xff0c;因此&#xff0c;当需要执行特殊行为时&#xff0c;客户代码就可以在运行时根据需要有选择地、按顺序地使用装饰功能包装对象。 案例&#xff1a;穿搭。衣柜有帽子、眼镜、…

Zabbix自动发现机制

Zabbix的自动发现机制 Zabbix客户端主动的和服务端联系&#xff0c;将自己的地址和端口发送服务端&#xff0c;实现自动添加监控主机&#xff0c;客户端是主动的一方缺点自定义网段中主机数量太多&#xff0c;等级耗时会很久&#xff0c;而且这个自动发现机制不是很稳定 Zabb…