当前位置: 首页 > news >正文

node 做的网站后端/谷歌paypal官网入口

node 做的网站后端,谷歌paypal官网入口,ui工程师工资一般多少,长春个人做网站线程安全 问题 一. 线程不安全的典型例子二. 线程安全的概念三. 线程不安全的原因1. 线程调度的抢占式执行2. 修改共享数据3. 原子性4. 内存可见性5. 指令重排序 一. 线程不安全的典型例子 class ThreadDemo {static class Counter {public int count 0;void increase() {cou…

线程安全 问题

  • 一. 线程不安全的典型例子
  • 二. 线程安全的概念
  • 三. 线程不安全的原因
    • 1. 线程调度的抢占式执行
    • 2. 修改共享数据
    • 3. 原子性
    • 4. 内存可见性
    • 5. 指令重排序

一. 线程不安全的典型例子

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

多次执行的结果:

在这里插入图片描述
两个线程各 加了 50000 次, 但最终结果都不是我们预期的 100000, 并且相差甚远。

二. 线程安全的概念

操作系统调度线程是随机的(抢占式),正因为这样的随机性,就可能导致程序的执行出现一些 bug。

如果多线程环境下代码运行的结果是符合我们预期的,即在单线程环境应该的结果,则说这个程序是线程安全的,否则就是线程不安全的。

三. 线程不安全的原因

1. 线程调度的抢占式执行

线程是抢占式执行,线程间的调度就充满随机性。
这是线程不安全的万恶之源,但是我们无可奈何,无法解决。

2. 修改共享数据

多个线程针对同一变量进行了修改操作,假如说多个线程针对不同变量进行修改则没事,多个线程针对相同变量进行读取也没事。

上面的线程不安全的代码中, 涉及到多个线程针对 counter.count 变量进行修改.
此时这个 counter.count 是一个多个线程都能访问到的 “共享数据”

在这里插入图片描述

counter.count 这个变量就是在堆上. 因此可以被多个线程共享访问.

3. 原子性

针对变量的操作不是原子的,通过加锁操作,可以把多个操作打包成一个原子操作。

一条 java 语句不一定是原子的,也不一定只是一条指令
比如上面的 count++,其实是由三步操作组成的:
(1)从内存把数据 count 读到 CPU
(2)进行数据更新 count = count + 1
(3)把更新后的数据 count 写回到 内存

所以说导致上面那段代码线程不安全的原因就是:

在这里插入图片描述

只要 t2 是在 t1 线程 save 之前读的, t2 的自增就会覆盖 t1 的自增, 那么两次加 1 的效果都相当于只加了 1 次.
所以上面的代码的执行结果 在 5w ~ 10w,并且大多数是靠近 5w 的。
(小于 5w 的是非常少见的, 这种情况就是 t2 线程覆盖了 t1 线程的多次 自增操作, 也就是说 t2 线程的 load 与 save 之间跨度很大的情况.)

解决:加锁 ! 打包成原子操作。
最常见的加锁方式就是 使用 synchronized

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

在这里插入图片描述

在自增之前加锁,自增之后再解锁。
当一个线程加锁成功时,其他线程再尝试加锁就会阻塞,直到占用锁的线程将锁释放。

那这样不就是串行执行了嘛?那多线程又有什么用 ?
实际开发中,一个线程要执行的任务很多,可能只有其中的很少一部分会涉及到线程安全问题,才需要加锁,而其他地方都能并发执行。

注意:
加锁,是要明确指出对哪个对象进行加锁的,如果两个线程对同一个锁对象进行加锁才会产生锁竞争(阻塞等待),如果对不同的对象进行加锁,那么不会产生锁竞争(阻塞等待)。
上面代码中 synchronized 加在方法上,那么就是对 Counter 对象加锁,对应到代码中就是两个线程就是对 counter 这个实例对象加锁。

4. 内存可见性

可见性指, 一个线程对共享变量值的修改,能够及时地被其他线程看到.

Java 内存模型 (JMM): Java虚拟机规范中定义了Java内存模型.
目的是屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的并发效果.

在这里插入图片描述

  • 线程之间的共享变量存在 主内存 (Main Memory).
  • 每一个线程都有自己的 “工作内存” (Working Memory) .
  • 当线程要读取一个共享变量的时候, 会先把变量从主内存拷贝到工作内存, 再从工作内存读取数据.
  • 当线程要修改一个共享变量的时候, 也会先修改工作内存中的副本, 再同步回主内存.

实际并没有这么多 “内存”. 这只是 Java 规范中的一个术语, 是属于 “抽象” 的叫法.
所谓的 “主内存” 才是真正硬件角度的 “内存”. 而所谓的 “工作内存”, 则是指 CPU 的寄存器和高速缓存.

由于每个线程有自己的工作内存, 这些工作内存中的内容相当于同一个共享变量的 “副本”. 此时修改线程 1 的工作内存中的值, 线程 2 的工作内存不一定会及时变化.

举一个栗子:
针对同一个变量:
一个线程进行循环读取操作,另一个线程在某个时机进行了修改操作。

在这里插入图片描述

比如某个代码中要连续 10 次读取某个变量的值, 如果 10 次都从内存读, 速度是很慢的比寄存器慢 3-4 个数量级. 
但是如果只是第一次从内存读, 读到的结果缓存到 CPU 的某个寄存器中, 那么后 9 次读数据就不必直接访问
内存了. 效率就大大提高了. (编译器优化的结果)

解决:

  1. 使用 synchronized
    synchronized 不仅能保证原子性,同时能保证内存可见性,
    被 synchronized 修饰的代码,编译器不会轻易优化。

  2. 使用 volatile 关键字
    volatile 和原子性无关,但是能保证内存可见性,禁止编译器优化,每次都要从内存中读取变量。

5. 指令重排序

什么是代码重排序?
举个栗子:
一段代码是这样的:

1. 去前台取下 U2. 去教室写 10 分钟作业
3. 去前台取下快递

如果是在单线程情况下,JVM、CPU指令集会对其进行优化,比如,按 1->3->2的方式执行,也是没问题,可以少跑一次前台,提高效率。这种叫做指令重排序。

编译器对于指令重排序的前提是 “保持逻辑不发生变化”.
这一点在单线程环境下比较容易判断, 但是在多线程环境下就没那么容易了,
多线程的代码执行复杂程度更高, 编译器很难在编译阶段对代码的执行效果进行预测,
因此激进的重排序很容易导致优化后的逻辑和之前不等价.

解决:
使用 volatile 关键字
volatile 除了能保证内存可见性之外还能防止指令重排序。

好啦! 以上就是对 线程安全 问题的讲解,希望能帮到你 !
评论区欢迎指正 !

相关文章:

【多线程】线程安全 问题

线程安全 问题 一. 线程不安全的典型例子二. 线程安全的概念三. 线程不安全的原因1. 线程调度的抢占式执行2. 修改共享数据3. 原子性4. 内存可见性5. 指令重排序 一. 线程不安全的典型例子 class ThreadDemo {static class Counter {public int count 0;void increase() {cou…...

【用unity实现100个游戏之11】复刻经典消消乐游戏

文章目录 前言开始项目开始一、方块网格生成二、方块交换三、添加交换的动画效果四、水平消除检测五、垂直消除检测六、完善删除功能七、效果优化&#xff08;移动方块后再进行消除检测&#xff09;八、方块下落十、方块填充十一、后续 源码参考完结 前言 欢迎来到经典消消乐游…...

若依cloud 修改包名等

一、项目的项目名。 先改pom 然后在重命名文件 1、 修改主pom.xml <artifactId>ruoyi-api</artifactId> 缓存 <artifactId>zxf-api</artifactId> <groupId>com.ruoyi</groupId> <groupId>com.zhixiaofeng</groupId> 2、…...

健康系统练习

健康系统 项目建构&#xff1a; 前后端分离&#xff0c;前端vue3&#xff0c;后端Java&#xff0c;springboot做跨域处理&#xff0c;前端将在vscode中 的tomcat下部署&#xff0c;后端将在ideal中集成的tomcat中部署 创建项目工程在ideal中直接选用springi…创建&#xff0c…...

网络协议从入门到底层原理学习(一)—— 简介及基本概念

文章目录 网络协议从入门到底层原理学习&#xff08;一&#xff09;—— 简介及基本概念一、简介1、网络协议的定义2、网络协议组成要素3、广泛的网络协议类型网络通信协议网络安全协议网络管理协议 4、网络协议模型对比图 二、基本概念1、网络互连模型2、计算机之间的通信基础…...

centos密码过期导致navicat无法通过SSH登录阿里云RDS问题

具体错误提示&#xff1a;2013 - Lost connection to server at "hand hake: reading initial communication packet, system error: 0 解决办法&#xff1a;更新SSH服务器密码...

对于pytorch和对应pytorch网站的探索

一、关于网站上面的那个教程: 适合PyTorch小白的官网教程&#xff1a;Learning PyTorch With Examples - 知乎 (zhihu.com) 这个链接也是一样的&#xff0c; 总的来说&#xff0c;里面讲了这么一件事: 如果没有pytorch的分装好的nn.module用来继承的话&#xff0c;需要设计…...

和AI聊天:动态规划

动态规划 动态规划&#xff08;Dynamic Programming&#xff0c;简称 DP&#xff09;是一种常用于优化问题的算法。它解决的问题通常具有重叠子问题和最优子结构性质&#xff0c;可以通过将问题分解成相互依赖的子问题来求解整个问题的最优解。 动态规划算法主要分为以下几个步…...

微信小程序——使用插槽slot快捷开发

微信小程序的插槽&#xff08;slot&#xff09;是一种组件化的技术&#xff0c;用于在父组件中插入子组件的内容。通过插槽&#xff0c;可以将父组件中的一部分内容替换为子组件的内容&#xff0c;实现更灵活的组件复用和定制。 插槽的使用步骤如下&#xff1a; 在父组件的wx…...

大数据技术之Hadoop:使用命令操作HDFS(四)

目录 一、创建文件夹 二、查看指定目录下的内容 三、上传文件到HDFS指定目录下 四、查看HDFS文件内容 五、下载HDFS文件 六、拷贝HDFS文件 七、HDFS数据移动操作 八、HDFS数据删除操作 九、HDFS的其他命令 十、hdfs web查看目录 十一、HDFS客户端工具 11.1 下载插件…...

静态路由配置实验:构建多路由器网络拓扑实现不同业务网段互通

文章目录 一、实验背景与目的二、实验拓扑三、实验需求四、实验解法1. 配置 IP 地址2. 按照需求配置静态路由&#xff0c;实现连接 PC 的业务网段互通 摘要&#xff1a; 本实验旨在通过配置网络设备的IP地址和静态路由&#xff0c;实现不同业务网段之间的互通。通过构建一组具有…...

Python函数的概念以及定义方式

一. 前言 嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! python更多源码/资料/解答/教程等 点击此处跳转文末名片免费获取 二. 什么是函数&#xff1f; 假设你现在是一个工人&#xff0c;如果你实现就准备好了工具&#xff0c;等你接收到任务的时候&#xff0c; 直接带上工…...

【数学建模竞赛】超详细Matlab二维三维图形绘制

二维图像绘制 绘制曲线图 g 是表示绿色 b--o是表示蓝色/虚线/o标记 c*是表示蓝绿色(cyan)/*标记 ‘MakerIndices,1:5:length(y) 每五个点取点&#xff08;设置标记密度&#xff09; 特殊符号的输入 序号 需求 函数字符结构 示例 1 上角标 ^{ } title( $ a…...

2023国赛数学建模E题思路代码 黄河水沙监测数据分析

E题最大的难度是数据处理&#xff0c;可以做一个假设&#xff0c;假设一定时间内流量跟含沙量不变&#xff0c;那么我们可以对数据进行向下填充&#xff0c;把所有的数据进行合并之后可以对其进行展开特性分析&#xff0c;在研究调水调沙的实际效果时&#xff0c;可以先通过分析…...

窗口延时、侧输出流数据处理

一 、 AllowedLateness API 延时关闭窗口 AllowedLateness 方法需要基于 WindowedStream 调用。AllowedLateness 需要设置一个延时时间&#xff0c;注意这个时间决定了窗口真正关闭的时间&#xff0c;而且是加上WaterMark的时间&#xff0c;例如 WaterMark的延时时间为2s&…...

发送HTTP请求

HTTP请求是一种客户端向服务器发送请求的协议。它是基于TCP/IP协议的应用层协议&#xff0c;用于在Web浏览器和Web服务器之间传输数据。 HTTP请求由以下几个部分组成&#xff1a; 请求行&#xff1a;包含请求方法、请求的URL和HTTP协议的版本。常见的请求方法有GET、POST、PUT、…...

高等工程数学张韵华版第四章课后题答案

下面答案仅供参考&#xff01; 章节目录 第4章 欧氏空间和二次型 4.1内积和欧氏空间 4.1.1内积的定义 4.1.2欧氏空间的性质 4.1.3 正交投影 4.1.4 施密特正交化 4.2 正交变换和对称变换 4.2.1 正交变换 4.2.2 正交矩阵 4.2.3 对称变换 4.2.4 对称矩阵 4.3 二…...

wpf C# 用USB虚拟串口最高速下载大文件 每包400万字节 平均0.7s/M,支持批量多设备同时下载。自动识别串口。源码示例可自由定制。

C# 用USB虚拟串口下载大文件 每包400万字节 平均0.7s/M。支持批量多设备同时下载。自动识别串口。可自由定制。 int 32位有符号整数 -2147483648~2147483647 但500万字节时 write时报端口IO异常。可能是驱动限制的。 之前用这个助手发文件&#xff0c;连续发送&#xff0…...

代码随想录二刷day20

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、力扣654. 最大二叉树二、力扣617. 合并二叉树三、力扣700. 二叉搜索树中的搜索四、力扣98. 验证二叉搜索树 前言 一、力扣654. 最大二叉树 /*** Definitio…...

Yolov5如何训练自定义的数据集,以及使用GPU训练,涵盖报错解决

本文主要讲述了Yolov5如何训练自定义的数据集&#xff0c;以及使用GPU训练&#xff0c;涵盖报错解决&#xff0c;案例是检测图片中是否有救生圈。 最后的效果图大致如下&#xff1a; 效果图1效果图2 前言 系列文章 1、详细讲述Yolov5从下载、配置及如何使用GPU运行 2、…...

设计模式之单列模式

单列模式是一种经典的设计模式&#xff0c;在校招中最乐意考的设计模式之一~ 设计模式就是软件开发中的棋谱&#xff0c;大佬们针对一些常见的场景&#xff0c;总结出来的代码的编写套路&#xff0c;按照套路来写&#xff0c;不说你写的多好&#xff0c;至少不会太差~ 在校招中…...

linux内核模块编译方法详解

文章目录 前言一、静态加载法1.1 编写驱动程序1.2 将新功能配置在内核中1.3为新功能代码改写Makefile1.4 make menuconfig界面里将新功能对应的那项选择为<*> 二、动态加载法2.1 新功能源码与Linux内核源码在同一目录结构下2.2 新功能源码与Linux内核源码不在同一目录结构…...

简介shell的关联数组与普通数组

本文首先介绍shell的关联数组&#xff0c;然后介绍shell的普通数组&#xff0c;最后总结它们的共同语法。 shell的关联数组 定义一个关联数组&#xff0c;并打印它的key-value对 #!/bin/sh# 声明一个关联数组 declare -A HASH_MAP# 给关联数组赋值 HASH_MAP["Tom"…...

玩转Mysql系列 - 第17篇:存储过程自定义函数详解

这是Mysql系列第17篇。 环境&#xff1a;mysql5.7.25&#xff0c;cmd命令中进行演示。 代码中被[]包含的表示可选&#xff0c;|符号分开的表示可选其一。 需求背景介绍 线上程序有时候出现问题导致数据错误的时候&#xff0c;如果比较紧急&#xff0c;我们可以写一个存储来…...

自动驾驶:轨迹预测综述

自动驾驶&#xff1a;轨迹预测综述 轨迹预测的定义轨迹预测的分类基于物理的方法&#xff08;Physics-based&#xff09;基于机器学习的方法&#xff08;Classic Machine Learning-based&#xff09;基于深度学习的方法&#xff08;Deep Learning-based&#xff09;基于强化学习…...

【uniapp/uview】u-datetime-picker 选择器的过滤器用法

引入&#xff1a;要求日期选择的下拉框在分钟显示时&#xff0c;只显示 0 和 30 分钟&#xff1b; <u-datetime-picker :show"dateShow" :filter"timeFilter" confirm"selDateConfirm" cancel"dateCancel" v-model"value1&qu…...

如何使用Docker部署Nacos服务?Nacos Docker 快速部署指南: 一站式部署与配置教程

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…...

yocto stm32mp1集成ros

yocto stm32mp1集成ros yocto集成ros下载meta-rosyocto集成rosrootfs验证 yocto集成ros 本章节介绍yocto如何集成ros系统用来作机器人开发。 下载meta-ros 第一步首先需要下载meta-ros layer&#xff0c;meta-ros的链接如下&#xff1a;https://github.com/ros/meta-ros/tre…...

Linux 中的 chroot 命令及示例

Linux/Unix系统中的chroot命令用于更改根目录。Linux/Unix 类系统中的每个进程/命令都有一个称为root 目录的当前工作目录。它更改当前正在运行的进程及其子进程的根目录。 在此类修改的环境中运行的进程/命令无法访问根目录之外的文件。这种修改后的环境称为“ chroot监狱”或…...

oracle的redo与postgreSQL的WAL以及MySQL的binlog区别

Oracle的redo日志、PostgreSQL的WAL(Write-Ahead Log)以及MySQL的binlog(二进制日志)都是数据库的事务日志,但它们在实现和功能上有一些区别。 1. 实现方式: - Oracle的redo日志是通过在事务提交前将事务操作记录到磁盘上的重做日志文件中来实现的。 - PostgreSQL…...