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

这就是我们上一篇中代码提到的加锁的主要方式,本质上是调用系统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

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

什么是可靠性测试,常见的可靠性测试标准有哪些?

1、可靠性试验背景介绍 为了测定、验证或提高产品可靠性而进行的试验称为可靠性试验&#xff0c;它是产品可靠性工作的一个重要环节。 2、通常&#xff0c;对产品进行可靠性试验的目的如下&#xff1a; (1)在研制阶段使产品达到预定的可靠性指标。为了使产品能达到预定的可靠性…

Python绘图坐标轴数字要求三位分节的处理方法

比如说1000&#xff0c;用三位分节法的写法就是1 000&#xff0c;咱们操作的时候可以先式化字符串&#xff0c;用千位分隔符表示数字就是1,000&#xff0c;再把逗号换成空格。 import matplotlib.pyplot as plt import matplotlib.ticker as ticker# 示例数据 x [1000, 2000, …

品牌要随时监测电商价格现实吗

电商渠道中的价格信息如果存在低价&#xff0c;那需要及时治理&#xff0c;否则低价会蔓延开来&#xff0c;影响渠道的发展&#xff0c;所以在治理前的监测工作非常重要&#xff0c;监测越全面&#xff0c;越准确&#xff0c;品牌进行渠道管控时会更有方向感&#xff0c;治理成…

Lattice-Based Blind Signatures: Short, Efficient, and Round-Optimal

目录 笔记后续的创新方向摘要引言 Lattice-Based Blind Signatures: Short, Efficient, and Round-Optimal CCS 2023 笔记 该文档提出了一种基于格子密码学的2轮盲签名协议。该协议是四舍五入最优的&#xff0c;签名大小为 22 KB&#xff0c;使其比其他基于格的方案更短。该文…

竞赛活动过程中评委亮灯是如何实现的

选秀节目中用到的那种评委爆灯效果要通过软件和硬件一起实现&#xff0c;软件实现在新一轮开始时&#xff0c;统一灭灯&#xff0c;评委通过按钮触发软件打开相应的灯&#xff0c;并自动发出声音。其实用到的物料包括&#xff1a;软件、按钮、灯、工业控制器。软件是核心&#…

Python Tkinter库入门与基础

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com Tkinter是Python标准库中内置的图形用户界面&#xff08;GUI&#xff09;工具包&#xff0c;提供了创建窗口、按钮、文本框等GUI元素的功能。本文将介绍Tkinter的基础知识&#xff0c;帮助大家快速入门。 安装与…

制作古风纹理的滕王阁3D模型

在线工具推荐&#xff1a; 3D数字孪生场景编辑器 - GLTF/GLB材质纹理编辑器 - 3D模型在线转换 - Three.js AI自动纹理开发包 - YOLO 虚幻合成数据生成器 - 三维模型预览图生成器 - 3D模型语义搜索引擎 滕王阁&#xff0c;位于江西省南昌市东湖区沿江路&#xff0c;地处赣…

PDM是什么?解析:PDM的基础知识

PDM是什么&#xff1f; PDM的中文名称为产品数据管理&#xff08;Product Data Management&#xff09;&#xff0c;它是一门用来管理所有与产品相关信息&#xff08;包括零件信息、配置、文档、CAD文件、结构、权限信息等&#xff09;和所有与产品相关过程&#xff08;包括过程…

HarmonyOS学习--初次下载安装和配置环境

一、Windows下载与安装软件 运行环境要求&#xff1a; 为保证DevEco Studio正常运行&#xff0c;建议电脑配置满足如下要求&#xff1a; 操作系统&#xff1a;Windows10 64位、Windows11 64位内存&#xff1a;8GB及以上硬盘&#xff1a;100GB及以上分辨率&#xff1a;1280*80…

智能优化算法应用:基于类电磁机制算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于类电磁机制算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于类电磁机制算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.类电磁机制算法4.实验参数设定5.算法结果…

【C】递归函数

一、什么是递归 递归其实是⼀种解决问题的⽅法&#xff0c;在C语⾔中&#xff0c;递归就是函数⾃⼰调⽤⾃⼰。 我们先了解一个知识&#xff1a; 每一次函数调用&#xff0c;都会向内存栈区上申请一块空间。 这块空间主要用来存放函数中的局部变量&#xff0c;和函数调用过程中…

不同角度范围下四元数转欧拉角的方式

前言 在标定过程中求出的欧拉角与预设真值差距太大&#xff0c;检查中发现求出的角度与真值角度都可以将车辆坐标系变换到相机坐标系。后通过查阅文献&#xff0c;发现四元数对应的欧拉角并不唯一&#xff0c;在不同的条件下可求出不同的欧拉角&#xff0c;实际应用中需根据实…

网络广播音柱在多场景中的应用

网络广播音柱在多场景中的应用 首先&#xff0c;网络音响在家庭娱乐方面有着突出的表现。在家里&#xff0c;我们可以通过它享受高质量的音乐、电影和游戏。无论是听悠扬的音乐旋律&#xff0c;还是看电影时震撼的音效&#xff0c;它都能提供逼真的沉浸式音效。此外&#xff0…

深入探索Python delattr函数的威力与灵活性

引言&#xff1a; 在Python中&#xff0c;delattr函数是一个非常强大且灵活的工具&#xff0c;它允许我们删除对象的属性。使用delattr函数&#xff0c;我们可以动态地删除对象的属性&#xff0c;从而在编程中实现更灵活的操作。本文将详细介绍delattr函数的用法&#xff0c;帮…

ABCDE类网络的划分及保留网段

根据IP地址的分类&#xff0c;IP地址被分为A、B、C、D和E五类。下面是对ABCDE类网络的划分及保留网段的详细描述&#xff1a; A类网络&#xff1a;范围从1.0.0.0到127.0.0.0&#xff0c;网络地址的最高位必须是“0”&#xff0c;可用的A类网络有127个&#xff0c;每个网络能容…

MQTT协议I/O模块:打通自动化生产线信息孤岛的创新力量

随着物联网的迅速发展&#xff0c;越来越多的IO设备需要与云平台进行通信&#xff0c;以实现远程监控和控制。 MQTT是通过发布主题来上传消息&#xff0c;订阅相关的主题来接收消息。钡铼技术I/O模块执行数据采集和数据处理后&#xff0c;将数据以发布MQTT主题消息的形式进行上…

中小企业:理解CRM与ERP系统的区别与联系,提升业务效能

许多中小型企业正面临着客户递增&#xff0c;市场营销&#xff0c;货存流通等递增数据整合的困扰。这个时候需要根据自身企业的实际情况去选择适合自己的系统。那么&#xff0c;中小企业使用CRM系统和erp系统的区别是什么&#xff1f; 一、含义和目标区别 CRM系统旨在帮助企业…

Qt/C++视频监控拉流显示/各种rtsp/rtmp/http视频流/摄像头采集/视频监控回放/录像存储

一、前言 本视频播放组件陆陆续续写了6年多&#xff0c;一直在持续更新迭代&#xff0c;视频监控行业客户端软件开发首要需求就是拉流显示&#xff0c;比如给定一个rtsp视频流地址&#xff0c;你需要在软件上显示实时画面&#xff0c;其次就是录像保存&#xff0c;再次就是一些…

数字化转型如何落地?_光点科技

数字化转型是现代企业发展的关键环节&#xff0c;它不仅仅是技术的升级&#xff0c;更是企业文化、运营模式和市场战略的全面革新。一个成功的数字化转型能够为企业带来更高效率、更好的客户体验和更强的市场竞争力。那么&#xff0c;数字化转型如何落地呢&#xff1f; 确定转型…

[PyTorch][chapter 5][李宏毅深度学习][Classification]

前言&#xff1a; 这章节主要讲解常用的分类器原理.分类主要是要找到一个映射函数 比如垃圾邮件分类 : c0, 垃圾邮件 c1 正常邮件 主要应用场景&#xff1a; 垃圾邮件分类,手写数字识别,金融信用评估. 这里面简单了解一下&#xff0c;很少用 目录&#xff1a; 1&#xff1a; …