线程安全问题(2)

news2024/12/28 18:03:55

锁的相关知识

1)这就是类似于说我们ATM机上面有一把锁,同一时刻,如果说人们之间不相互认识,那么通过这把锁就进行限制了说就限制了说一次只能有一个人来进来取钱,我们通过这样的锁,就可以来进行避免上述这种乱序排序执行的情况

2)特点:互斥的,同一时刻只有一个线程可以获取到锁,如果其他的线程也尝试获取到锁,就会发生阻塞等待,一直阻塞到刚才的线程释放锁,剩下的线程再尝试竞争锁

1)当一个线程加锁成功的时候,其他线程尝试加锁,就会进入到阻塞等待状态,此时我们对应的线程,就处于BLOCK状态,阻塞会一直持续到占用锁的线程把锁释放为止

2)咱们的加锁操作,就是把若干个不是原子的操作封装成一个原子的操作

3)同步:在多线程中,线程安全中,同步,本质上是指定的是互斥,只有一个人可以获取成功,其他竞争同类资源的只能失败

4)但是在IO或者是网络编程里面,同步相对的词叫做异步,此时同步和异步没有任何关系了,和线程也没有关系了,它表示的是消息的发送方是怎么获取到结果的

5)synchronized本质上来说是修改了对应对象的对象头里面的一个标记

 class Counter{
        public int count;
    }
Counter counter=new Counter();

1)我们对应的线程进入到increase之前会尝试加锁,直到我们对应的线程执行完成increase方法执行完后才会执行解锁

2)但是在编程中如果出现了一个线程获取到锁了,出问题了,自己不干活,还影响到其他线程获取到锁,那么该怎么办呢?后续会说的

3)我们在进行使用synchronized关键字,其实本质上就是在针对我们具体的某一个对象在进行加锁,我们正常情况下在我们创建的对象中会包含一个具体的字段,表示该对象的加锁状态,可以想象到是一个BOOLEAN类型,针对这个对象未加锁就显示为false,针对这个对象进行加锁就显示为true;

4)在数据结构中,加锁的有 Stringbuffer,vector,Hashtable

一)加入到普通方法前,相当于是针对当前this来进行加锁;

1)synchronized加到普通方法前,表示锁this(当前实例对象),就是设置this也就是普通对象的对象头的标记位,如果两个线程同时的并发去调用这个synchronized修饰的方法,尝试针对同一个对象来进行加锁的时候,本质上是修改了Object对象中的“对象头的”里面的一个标记,假设一开始默认情况下是false,线程1已经把这个对象的标志位设置成了true,此时线程2也尝试来进行获取到这把锁,但是此时因为标志为不是false,所以说我们就不能进行修改标志位,必须要等到标志位变成false的时候,或者说等到线程1出了synchronized修饰的方法了也就是将我们对象对应的标志位改成false之后,我们的线程2才可以进行获取到锁

2)进入到synchronized修饰的方法,就相当于是加锁;出了synchronized修饰的方法,就相当于是解锁;它的功能就相当于把并发变成串行,两个线程针对不同的对象来进行加锁,就不会产生竞争

3)适当的牺牲速度,换来的是结果的准确性;

4)如果此时是加锁的状态,其他的线程就会无法执行这里面的逻辑,就只能进行阻塞等待;

这就相当于是ATM机取钱,同一时刻只能有一个人进入到ATM机取钱,别的人想要进来,并且获取到锁,是会阻塞等待的;

//我们是针对当前this来进行加锁
    public synchronized void run()
    {
        System.out.println("我叫做run方法");
    }

1)进入到Synchronized修饰的方法或者同步代码块==将对象头的标志位从false改成true

2)退出synchronized修饰的方法或者同步代码快==将对象头的标志位从true修改成false

3)当我们的两个线程尝试针对同一个对象加锁的时候才会产生竞争,如果是两个线程针对不同的对象进行加锁,那么就不会产生竞争

4)这就类似于说两个人抢1个ATM机,会产生竞争,但是如果说两个线程同时抢两个不同的ATM机,那么就不会产生竞争

2)加到某个静态方法前表示锁当前类的类对象,对类对象进行加锁

就算我们把synchronized加到静态方法上面,静态方法上面是没有this的,因为和实例不相关,也就是说谈不上this,所谓的静态方法更应该叫做类方法,咱们的普通的方法,更严谨的叫法,就应该叫做实例方法

2.1)反射是面向对象的一个基本特性,和封装,继承,多态是并列关系;反射也叫做自省,是指一个对象可以认清自己,这个对象里面包含哪些属性,每个属性叫啥名字,是什么类型,包含哪些方法,每个方法叫啥名字,参数列表是什么,所以说反射机制就是.class文件赋予的力量

2.2)这些信息来自.class文件,它咱们在进行运行程序的时候,是.java文件编译生成的字节码文件,会在JVM运行的过程中加载到内存里面,就会通过类对象来描述这个.class文件的内容和一切信息,类名.class就得到了这个类对象,每个类的类对象都是单例的

2.3)由于类对象是单例的,多个线程并发调用被synchronized修饰的同步代码快况且是给类对象进行加锁或者是使用synchronized修饰时的静态方法,一定会触发锁竞争

package com;

class Counter{
    public static int count=0;
    public synchronized void increase(){
//这是针对当前Counter对象来进行加锁
        count++;
    }
}
public class Solution {
    private static Counter counter=new Counter();
    public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(()->{
            for(int i=0;i<10;i++){
                counter.increase();
            }
        });
        Thread t2=new Thread(()->{
            for(int i=0;i<10;i++){
                counter.increase();
            }
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(counter.count);
    }

}

三:如果说我们是针对某一个代码块进行加锁,就需要手动进行指定,锁对象是什么,synchronized后面要加一个括号,表示进行加锁的对象,针对哪一个对象来进行加锁,咱们JAVA中的任何对象,都可以作为锁对象

3.1)进入到synchronized修饰的同步代码快,相当于是加锁==给对象头的标志位设置从false设置成true;

3.2)出了到synchronized修饰的同步代码快,相当于是解锁==给对象头的标志位设置从true设置成了false;

  public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(()->{
            for(int i=0;i<10;i++){
                synchronized(Object.class){
                    count++;
                }
            }
        });
        Thread t2=new Thread(()->{
            for(int i=0;i<10;i++){
               synchronized(Object.class){
                   count++;
               }
            }
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(count);
    }

1)是如果括号里面的内容类型不同,也就是说对应到两个类对象(这时有两把锁 

就不会出现竞争)

2)但是如果两个对象是相同的类,就是一把锁,就会发生竞争,他们必须竞争同一把锁

1)如果两个线程,尝试针对同一个锁对象进行加锁,此时一个线程会先获取到锁,另一个线程就会阻塞等待;
2)如果两个线程,尝试对不同的锁对象进行加锁,此时两个线程都可以获取到锁,互不冲突;

3)synchronized会进行记录是当前哪一个线程所持有的锁,当线程一获取到这个锁的时候,在进入到代码块,此时就知道线程一已经获取到了这个锁,就不会进行阻塞了;

回顾内存可见性:

 线程的核心代码中,循环其实没干啥,反复地执行循环条件的比较操作,具体情况如下

1)先从内存中读取flag的值到CPU中

2)在CPU中执行比较这个数与0之间的相等关系

3)从CPU上的寄存器读取数据速度要比从内存中读数据要快得多

1)编译器在判断这个逻辑中,循环也没干什么事情,只是频繁的读内存而已,于是编译器就把这个过程优化了,第一次把内存数据读到CPU后,后续的都不是真的从内存中读了,而是读取刚才在CPU中读的数据。

2)编译器认为Flag没有出现改动,其实只是在当前线程中没有出现改动,而编译器不能感知其他线程对flag进行了修改,于是就发生了误判,其实编译器是好心,但是你优化过的逻辑必须的和原来的逻辑是等价的呀!!!!

3)一个线程读,一个线程写;两个线程都写,那就用volatile来解决,禁止编译器过度优化

4)加了volatile 后,对这个数据的读取肯定是在内存中读取,禁止刚才的优化;但是他不可以保证原子性

5)编译器什么时候进行优化,什么时候不进行优化,我们是把握不住的;如果在循环里面加入sleep(1000),降低循环的速率,可能编译器就不会进行优化了;

总结:一个线程频繁读,另一个线程写,大概率会用到volatile;

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

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

相关文章

ESP8266 Arduino开发 搭建web服务器与客户端开发

一、wifi 相关配置 1.1 无线终端 wifi 模式 此模式中&#xff0c;esp8266 会连接到指定 wifi 进行工作。 #include <ESP8266WiFi.h> // 本程序使用ESP8266WiFi库const char* ssid "home"; // 连接WiFi名&#xff08;此处使用home为示例&…

位运算相关

1.与运算 &#xff08;点我&#xff09; 这个题的大概意思&#xff1a;给222个数nnn和xxx&#xff0c;其中满足n&(n1)&(n2)&(n3)...&mxn\&(n1)\&(n2)\&(n3)...\&mxn&(n1)&(n2)&(n3)...&mx,求最小的mmm&#xff0c;只要满足m&g…

【异常】java11提示: Cannot find any provider supporting RSA/ECB/PKCS1Padding的问题

一、背景 项目中需要对敏感字段进行加密&#xff0c;但是加密方法中涉及到比较复杂的加密算法&#xff0c;这些算法都需要一个Provider&#xff0c;主要是用于初始化算法的。 以下是遇到的具体问题 二、报错截图 java.security.NoSuchAlgorithmException: Cannot find any pr…

【软件测试】软件测试模型

1. V模型 需求分析—计划—设计—编码—测试 ● 概要设计&#xff1a;设计整体架构&#xff0c;框架 ● 详细设计&#xff1a;模块和模块之间的详细设计 ● 集成测试&#xff0c;单元测试&#xff1a;通常由开发人员进行 特点&#xff1a; 明确标注了测试的多类型明确标注了测…

Introduction to Multi-Armed Bandits——01 Scope and Motivation

Introduction to Multi-Armed Bandits——01 Scope and Motivation 参考资料 Slivkins A. Introduction to multi-armed bandits[J]. Foundations and Trends in Machine Learning, 2019, 12(1-2): 1-286.项目地址 https://github.com/yijunquan-afk/bandit-learning Bandit…

LeetCode622.设计循环队列

设计循环队列1.题目描述2.思路3.代码实现以及分析3.1 创建结构体3.2创建一个具体的循环队列3.3判断是否为空 和 判断是否为满4. 进队列 和 出队列5.取队首和队尾元素6.释放空间7.总结1.题目描述 设计循环队列 2.思路 环形队列的抽象图 我们这里使用数组模拟实现循环队列&…

TransactionTemplate自动注入,只看这一篇文章就够了

标准的springboot接入mybatis步骤 1.引入了对应的依赖包 2.应用的properties下增加相应配置 3.根据配置进行自动装配 一般我们会配置这些信息&#xff0c;主要包括三类 1.数据库的连接信息 2.指定的数据源类型 3.mybatis的配置信息 配完以后&#xff0c;当你启动SpringBoot的主…

你是真的“C”——详解C语言数组模块知识

详解C语言数组模块知识&#x1f60e;前言&#x1f64c;一维数组的创建和初始化&#x1f64c;1.1 数组的创建&#x1f49e;1.2 数组的初始化&#x1f49e;1.3 一维数组的使用&#x1f49e;1.4 一维数组在内存中的存储&#x1f49e;二维数组的创建和初始化&#x1f64c;1.1 二维数…

【Python百日进阶-数据分析】Day225 - plotly的Ohlc图go.Ohlc()

文章目录一、语法二、参数三、返回值四、实例4.1 简单的OHLC图4.2 隐藏滑块的OHLC图4.3 添加自定义文本和注释4.4 自定义OHLC颜色4.5 带日期时间对象的简单的OHLC图4.6 自定义悬浮文本4.7 Dash中的应用一、语法 ohlc&#xff08;Open-High-Low-Close 的缩写&#xff09;是一种…

【C++逆向】虚表(Virtual table)逆向 | 安卓so虚函数逆向

什么是多态 定义一个虚基类ISpeaker class ISpeaker{ protected:size_t b; public:ISpeaker( size_t _v ): b(_v) {}virtual void speak() 0; };有两个子类&#xff0c;都实现了虚函数speak()&#xff1a; class Dog : public ISpeaker { public:Dog(): ISpeaker(0){}//vir…

1581_AURIX_TC275_SMU故障处理梳理

全部学习汇总&#xff1a; GreyZhang/g_TC275: happy hacking for TC275! (github.com) 前面为了缓解自己的学习压力&#xff0c;一次学习笔记大概也就是看10页文档整理一下。这一次其实是看了几十页&#xff0c;但是里面过掉了一些信息&#xff0c;而且这部分内容不是很好拆分…

hive在IDEA中debug

一、hive在IDEA中debug 安装hadoop环境&#xff08;1和2替换顺序也可以&#xff09; 注&#xff1a;hadoop环境不需要从源码编译 https://hadoop.apache.org/docs/stable/hadoop-project-dist/hadoop-common/SingleCluster.html 按照官网教程编译源码 https://cwiki.apach…

软件工程专业课实验报告

一、结构化分析方法1.1需求描述教务管理子系统的需求描述&#xff1a;教务管理是一项需求周密计划、严谨安排的工作&#xff0c;要依据教师、学生信息进行合理安排。开学阶段&#xff0c;需要教师提交开课申请进行开课&#xff0c;学生根据老师的开课信息&#xff0c;选择课程&…

uview 使用遇到的问题。

uviewuniappvue&#xff0c;uView是uni-app生态专用的UI框架。 1. 注意uview版本&#xff0c;uview 2.0与uview1.0 官方提示&#xff1a;uView2.0是继1.0以来的一次重大更新&#xff0c;2.0已全面兼容nvue。 因此在接手项目的时候首先得看清楚&#xff0c;之前开发的是uview…

【自学Python】Python获取字符串长度

Python获取字符串长度 Python获取字符串长度教程 在 Python 中要想获取 字符串 长度可以使用 len() 函数。 Python len()函数详解 定义 我们将要获取的字符串的长度&#xff0c;传进 len() 函数&#xff0c;即可实现获取字符串的长度。 语法 len(string)参数 参数描述s…

【7】K8s_Ingress | Service的统一网关入口

目录 1、Ingress简介 2、安装ingress 【1】制作ingress.yaml文件并执行 【2】测试&#xff0c;创建一个test.yaml文件并执行 【3】设置域名访问&#xff0c;用yaml文件 【4】路径重写 【5】流量限制 1、Ingress简介 Ingress: Service的统一网关入口是k8s中的一个api对象&…

时序数据库TDengine基本概念和建模思路

目录 一 、 时序数据库基本概念 采集量 标签 数据采集点 表 超级表 子表 库 二、 TDengine数据库建模策略 建表模式建表情形 行列数据库存储的区别&#xff1a; 接触的传统业务的数据模式都是行存储&#xff0c;我们会把不同类型的对象创建不同的表进行存储他们各自的属…

机器学习公式推导与代码实现-无监督学习模型

聚类分析与k均值聚类算法 督学习算法。在给定样本的情况下,聚类分析通过度量特征相似度或者距离,将样本自动划分为若干类别。 距离度量和相似度度量方式 距离度量和相似度度量是聚类分析的核心概念,大多数聚类算法建立在距离度量之上。常用的距离度量方式包括闵氏距离和马…

linux系统中使用QT来实现数据库的调用方法

大家好&#xff0c;今天主要和大家分享一下&#xff0c;如何使用QT中数据库的使用方法。 目录 第一&#xff1a;数据库基本简介 第二&#xff1a;数据库表格基本操作 第三&#xff1a;数据库最终效果 第一&#xff1a;数据库基本简介 数据库是按照数据结构来组织&#xff0c;…

视频目标检测与轨迹跟踪代码案例

前言通过阅读相关文献及测试&#xff0c;找到了一种基于多模板匹配的改进方法&#xff0c;可以对遥感视频卫星中的移动目标进行探测&#xff0c;并绘制其轨迹。根据实验结果发现&#xff0c;可以比较有效的对运动目标进行跟踪。一、原理核心思想比较简单。即通过不同旋转角度的…