【并发编程】基础系列(一):线程安全性

Java并发编程基础小结 并发编程的核心问题与解决方案 本文从多线程的必要性、优势入手,深入分析了并发编程需要关注的三大核心问题:安全性问题(如数据竞争)、活跃性问题(如死锁、活锁)以及性能问题(如线程切换开销)。作者通过生动的代码示例和图示,展示了在多线程环境下可能出现的典型问题,并指出JVM中线程与进程的资源分配差异。文章还解释了程序计数器、虚拟机栈等线程私有区域的设计原理,以及如何利用"栈封闭"技术解决线程安全问题。全文为Java开发者提供了对并发编程本质问题的系统性思考框架。 阅读详情

简介

随着计算机硬件的飞速发展,处理器的数量持续增长,要想充分发挥多处理器系统的强大计算能力,就需要使用多线程,因此高效的并发变得越来越重要。本篇将带领大家进入并发编程领域,说到并发编程,不得不提的就是线程安全性。

在构建稳健安全的并发程序的时候,必须正确的使用线程和锁,但这只是实现机制,核心在于对状态访问操作进行管理,尤其是共享状态(Shared)和可变状态(Mutable),对象的状态,可以理解为存储在状态变量(例如实例或者静态域)中的数据。Shared意味着变量可以由多个线程同时访问,而Mutable意味着变量的值在其生命周期内可以发生变化。
一个线程是否是线程安全的,主要取决于它是否被多个线程同时访问,当多个线程访问某个状态变量且至少有一个线程是执行写入操作时,则必须采用同步机制,Java中主要的同步机制是关键字synchronized,它提供的是一种独占的加锁方式,“同步”这个术语还包括了volatile类型的变量、显式锁Explicit Lock以及原子变量。

1. 线程安全性的概念

要给线程安全性一个准确的定义是非常复杂的,各大资料都众说纷纭,但只要能抓住关键就可以,需要明确,在线程安全性的定义中,最核心的概念就是正确性,而正确性的含义是,某个类的行为与其规范完全一致。在对“正确性”有了明确的理解之后,就可以定义线程安全性了:

当多个线程访问某个类时,这个类始终都能表现出正确的行为,那么就称这个类是线程安全的。

在开发中想要快速判断一个类是否是线程安全的时候,只需要知道线程安全的充分条件:

在多个线程访问某个类时,不管采用何种调度方式或者这些线程如何交替执行,在主调代码中都无需添加额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类时线程安全的。

Tips:
无状态对象一定是线程安全的。所谓无状态对象,指的是既不包含任何域,也不包含任何对其他类中域的引用。

2. 原子性

2.1 问题发现

先来看这么一个问题,若想统计Servlet中请求的数量,定义了一个计数器count并在每次调用service方法的时候自增,程序清单如下:

/**
 * @author Carson
 * @date 2020/1/18 13:20
 * @description
 */
@NotThreadSafe
public class UnsafeCountingFactorizer implements Servlet {
    private long count=0;
    
    public void service(ServletRequest servletRequest,ServletResponse servletResponse){
        /* 处理请求的具体逻辑,省略 */
        ++count;
    }
}

计数器的递增操作++count虽然看上去是个紧凑的语法,但事实上并非原子的,因为它包含了三个独立的操作:读取count的值,将值加1,然后将计算结果写入内存中的count,这其实是一个“读取——修改——写入”的操作序列,并且每一步的结果状态都要依赖上一步,也就是说如果某一步顺序异常将会导致“脏数据”,带来数据完整性问题。如下图所示:
在这里插入图片描述
在上图中,count初始值为9,假设此时有两个线程A、B请求了service方法,计数器应该递增到11,然而,由于在线程A读取完但尚未完成自增和写入的时候,线程B也读取了count值,此时仍然是旧值9,线程B再进行自增、写入,显然,这样得到的count值与真实值有所偏移。

2.2 竞态条件(Race Condition)

在并发编程中,这种由于不恰当的执行顺序而出现不正确的结果是一种非常重要的情况,即Race Condition。
最常见的竞态条件就是“先检查后执行(Check-Then-Act)”,即通过一个可能已经失效的观测结果来决定下一步的动作。事实上大多数竞态条件的本质就是基于一种可能失效的观测结果来做出判断或者执行某个计算,比如:当我们需要观察某个条件X,只有当X为真时,再采取相应的动作,但在观察到条件X和执行相应动作的期间,有可能别的线程过来修改了条件X,从而使之前对条件X的观察结果变得无效,进而造成各种问题(数据被覆盖,文件被破坏等)。
示例:延迟加载中的竞态条件
延迟初始化的目的是将对象的初始化操作推迟到实际被使用时才进行,同时要确保只被初始化一次。

/**
 * @author Carson
 * @date 2020/1/18 13:25
 * @description
 */
@NotThreadSafe
public class LazyInitRace {
    private Object instance = null;

    public Object getInstance() {
        if (instance == null) {
            instance = new Object();
        }
        return instance;
    }
}

在上述代码中,如果线程A和线程B同时执行getInstance,当A线程进入判断,看到instance为空,则创建了一个实例,当线程B再进入判断的时候,此时的instance是否为空,要取决于不可预测的时序,包括线程的调度方式,以及线程A需要多长时间来初始化Object并设置到instance对象中。如果当线程B检查时,instance为空,那么在两次调用getInstance时可能会得到不同的结果。

2.3 复合操作

首先给出概念,所谓复合操作:就是包含了一组必须以原子方式进行的操作以确保线程安全性。
上述两个类UnsafeCountingFactorizer 和LazyInitRace 都包含一组需要以原子方式执行的操作。要避免竞态条件,就必须保证在某个线程修改变量时,通过某种机制保证其它线程使用这个变量,从而确保其它线程只能在修改操作完成之前或者之后读取和修改状态。

假定有两个操作A和B,如果从执行A的线程来看,当另一个线程执行B时,要么将B全部执行完,要么完全不执行B,那么A和B对彼此来说都是原子的。原子操作是指,对于访问同一个状态的所有操作(包括该操作本身)来说,这个操作是一个原子方式执行的操作。

在上述的统计servlet请求的数量中,由于自增操作并不是原子的,所以存在线程安全性问题,可以通过采用Java中封装的原子变量类实现在数值和对象引用上的原子状态转换。

/**
 * @author Carson
 * @date 2020/1/18 14:25
 * @description
 */
@ThreadSafe
public class CountingFactorizer implements Servlet {
    private final AtomicInteger count = new AtomicInteger(0);

    public void service(ServletRequest servletRequest, ServletResponse servletResponse) {
        /* 处理请求的具体逻辑,省略 */
        count.incrementAndGet();
    }
}

3. 加锁机制

3.1 问题发现

假设现在为了提高Servlet的性能,需要把上一次的结果缓存起来,结果包含两个状态:1.最近执行因数分解的数值,2.分解结果。初步设计如下代码:

/**
 * @author Carson
 * @date 2020/1/20 21:49
 * @description
 */
@NotThreadSafe
public class UnsafeCachingFactorizer implements Servlet {
    /* 上次执行因式分解的数值 */
    private final AtomicReference<BigInteger> lastNumber = new AtomicReference<>();
    /* 上次执行因式分解的结果 */
    private final AtomicReference<BigInteger[]> lastFactors = new AtomicReference<>();

    @Override
    public void service(ServletRequest request, ServletResponse response) {
        BigInteger i = request.getNumber("number");
        /* 业务逻辑此处略去 */
        BigInteger[] factors = facotr(i);
        lastNumber.set(i);
        lastFactors.set(factors);
    }
}

观察上面的代码,不难发现,在使用原子引用的情况下,尽管对set方法的每次调用都是原子的,但仍然无法保证同时更新lastNumber和lastFactors。而如果两个状态没有同时更新的话,其它线程将发现不变性条件被破坏了。
Tips:
要保证状态的一致性,就必须要在单个原子操作中更新所有相关的状态变量。

3.2 内置锁

针对上述问题,Java自身有一种机制可以解决,那就是内置锁,也即同步代码块(Synchronized Block),同步代码块有两个重要部分,一是作为锁的对象引用,二是由这个锁保护的代码块。

	synchronized (lockObject){
        /* 由内置锁保护的共享状态 */
    }

Java中的每个对象都可以用做一个实现同步的锁,这些锁被称为内置锁(Intrinsic Lock)或监视器锁(Monitor Lock)。获得内置锁的唯一途径就是进入由这个锁保护的同步代码块或方法。Java内置锁相当于一种互斥体,这意味着最多只有一个线程能持有这种锁。当线程A尝试获取一个由线程B持有的锁时,线程A必须等待或者阻塞。
针对3.1中的代码,可以将service方法加锁实现:

	@Override
    public synchronized void service(ServletRequest request, ServletResponse response) {
        BigInteger i = request.getNumber("number");
        /* 业务逻辑此处略去 */
        BigInteger[] factors = facotr(i);
        lastNumber.set(i);
        lastFactors.set(factors);
    }

但由于synchronized是互斥体,所以并发性非常糟糕,每次只能有一个请求进入方法体。

3.3 重入

如果某个线程试图获得一个已经由它自己持有的锁,那么这个请求就会成功。“重入”意味着获取锁的操作的粒度是“线程”而不是“调用”。Java中的内置锁是可重入的。
关于重入的必要性,可以参看以下代码:

public class Widget{
    public synchronized void show(){
        /*  */
    }
}

public class subWidget extends Widget{
    @Override
    public synchronized void show(){
        System.out.println("Success");
        super.show();
    }
}

在以上程序清单中,子类重写了父类的show方法,然后调用父类中的方法,此时如果没有可重入的锁,那么这段代码将产生死锁。由于Widgt和subWidget 中的show方法都是synchronized修饰的方法,因此每个show方法在执行前都会获取Widget上的锁,如果内置锁是不可重入的,那么在调用super.show()方法时将无法获得Widget上的锁,因为这个锁已经被其自身持有,从而线程将永远等待下去。

4. 小结

对于可能由多个线程同时访问的可变状态变量,在访问它时都需要持有同一个锁,在这种情况下,我们称状态变量是由这个锁保护的。
一种常见的加锁约定是:将所有的可变状态都封装在对象的内部,并通过对象的内置锁对所有访问可变状态的代码进行同步,使得在该对象上不会发生并发访问。
当某个变量由锁保护时,意味着在每次访问这个变量时都需要首先获得锁,这样就确保在同一时刻只有一个线程可以访问这个变量。并且对于每个包含多个变量的不变性条件,其中涉及的所有变量都需要由同一个锁来保护。
在3.2节中通过synchronized对整个方法加锁,事实上这在生产中会引入活跃性和性能问题。我们可以通过缩小锁的粒度来提高并发性,即将同步方法优化为同步代码块:

@ThreadSafe
public class UnsafeCachingFactorizer implements Servlet {
    /* 上次执行因式分解的数值 */
    private final AtomicReference<BigInteger> lastNumber = new AtomicReference<>();
    /* 上次执行因式分解的结果 */
    private final AtomicReference<BigInteger[]> lastFactors = new AtomicReference<>();

    @Override
    public void service(ServletRequest request, ServletResponse response) {
        BigInteger i = request.getNumber("number");
        /* 业务逻辑此处略去 */
        BigInteger[] factors = facotr(i);
        synchronized (this) {
            lastNumber.set(i);
            lastFactors.set(factors);
        }
    }
}

Tips:
在执行时间较长的计算或者无法快速完成的操作(如网络I/O)时,一定不要让该操作持有锁。

el-table通过上下左右键移动光标 在Element Plus的el-table中包含el-input和el-select组件,要实现上下左右键控制光标位置,需要通过focus和blur跟踪判断当前是哪个el-input或el-select有焦点,当按键按下时,通过监听,计算下一个获取焦点元素的索引,通过focus方法将焦点切换至改元素。 阅读详情

相关推荐

DATCOM GUI——DATCOM-Tools使用介绍

简介 本工具以1976年美国空军稳定性与操纵性计算程序(USAF STABILITY AND CONTROL DIGITAL DATCOM PROGRAM)为基础进行开发,目的是提供更为简洁方便的外形输入方式,并带有三维绘图与气动特性输出功能。 安装与环境配置 1、 代码以Holy Cows公司提供的DATCOM程序为基础,首先下载其安装包(window,Linux均有)。 2、 安装到你想要的位置,注意不要有中文路径 3、 测试DATCOM是否能够正常运行。复制examples文件夹中的Citation

qq_40800981的博客 1万+

【Java并发编程系列】- 线程共享的安全性问题

共享指共享内存区域(堆,方法区),只有栈、本地方法栈、PC是线程私有的。多线程带来问题的原因是对共享变量的读写操作,不具原子性,并行情况下多个线程读写混乱;以及并发情况下线程上下文切换导致成员变量被修改以及没来及写回就被其他线程读,如两线程都对非局部变量count,count++,count--

m0_68383079的博客 1061

EtherCAT-1 技术概述

1.EtherCAT简介①开放的技术EtherCAT全称EtherNet Control Automation Technology,是由德国倍福(Beckhoff)公司提出的一种实时以太网技术。EtherCAT是一种开放但不开源的技术,意味着您可以任意使用这项技术,但若要进行相关设备的开发,则需要向倍福公司获取相关授权。②快速性相比传统现场总线,EtherCAT的数据传输速率有了极大的提升,可选10Mbit/s或100Mbit/s,甚至依托补充

tianya_xuan的博客 3543

并发编程基础:从线程到进程的Python实践

在现代编程中,并发编程是一项不可或缺的技能。无论是为了提升程序的运行效率,还是充分利用多核处理器的性能,掌握并发编程都能让你的代码更上一层楼。Python 提供了强大的工具,如threading和模块,让开发者能够轻松实现多线程和多进程编程。本文将围绕并发编程基础展开,从线程与进程的概念入手,逐步深入到如何在 Python 中使用threading模块实现多线程,以及使用模块实现多进程。文章将通过通俗易懂的语言、清晰的代码示例和实际应用场景,帮助初学者快速入门,同时为进阶读者提供实用的技术参考。

吴师兄大模型的博客 1284

PCRNet: Point Cloud Registration Network using PointNet Encoding

PCRNet算是在ModelNet40这种object-centric数据集上用deep-learning做配准最好入手的一篇文章了,整体架构涉及简洁明了,一句话描述就是简单粗暴,话不多说,直接上网络架构图: Work Flow: 首先对输入source (N×3N \times 3N×3)与template (M×3M \times 3M×3)进行PointNet操作,即MLP + max-pooling后,分别得到描述各自点云的维度为1024的global feature vector:Φ(PS).

dloading7的博客 1648

USB3.0一致性测试方案

为各测试项目具体测试结果包括脉冲串突发时间、脉冲串重复时间、时钟周期以及眼图等完整USB3.0测试项目,工程师可以看到各项测试的具体结果方便观察。RIGOL 提供了一套完善的工具,包括数字示波器、专业的测试夹具以及全自动一致性测试软件,使得USB 3.0设备设计人员能够高效精确地进行信号捕捉、测试与分析。报告第二部分测试摘要,在此可以看到USB3.0测试所有测试项目,并且可以观看到测试是否正常,可以帮助工程师更好的判断测试结果。,以简化下载、存储以及对于多媒体的大量内容的共享。

2409_87378937的博客 2530

Java并发编程学习2-线程安全性

线程安全性,竞态条件,加锁机制

Huazie 811

上证指数30年k线图_金融占星简单解读上证指数30年历史行情

阅读此文需要一定的占星基础知识,本人对于金融知识了解甚少,如有描述错误请包涵指正!影响大众事件发展趋势的都是外行星,即木星-冥王星这5颗,由于时间跨度长达30年,绘制社会行星的K线图工作量太大,所以只以时代行星对这30年行情的简单解读作为分享,在今后的短期分析中会逐渐加入社会行星,甚至在个股分析上还会加入个人行星。那即使只绘制时代行星,所展现出来的信息也无法在一张图上画出来,否则太乱难以分辨,所以...

weixin_39831242的博客 1675

Java并发编程之线程安全性

最近恰好有点时间看《Java并发编程实践》,以前没有写过博客,顺手写一下笔记和自己的感悟。 目录 目录 简介 并发简史 线程的优势 线程的风险 线程安全性 什么是线程安全性 简介 并发简史 在操作系统出现之前,早期的计算机只能从头到尾执行一个独占所有计算机资源的程序,不仅很难编写和运行程序,而且每次只能运行一个程序, 这对昂贵和稀有的计算机资源是一

Mind And Hand 632

【感知模块算法】障碍物detect+trace+prediction方向介绍

系列文章目录 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 TODO:写完再整理 文章目录系列文章目录前言一、多维度看感知问题,体会复杂性二、障碍物/环境目标detect方向1.视觉目标检测方向(1)图像处理认知(2)视觉基础理论部分1、获取数据集---python网页爬虫、买数据、自己录数据、使用开源比赛的数据集2、opencv库的学习3、深度学习框架学习(3)视觉目标检测理论(1)目标检测的概念(2)目标检测的应用场景(3)现有目标检测框架原理及论文【这仅仅是我当初入门学习的框架~

qq_35635374的博客 2894

RIP协议

一、 Rip简介 早期在unix系统上使用,1988年才发布第一个rip协议标准“RFC1058”,是一种距离矢量(Distance-vector)协议。 1. 特点: 只关心跳数,下一跳,不关心整网拓扑。 基于传闻的路由协议。 2. 场景: 适用于规模小且可靠性要求低的网络环境,部署简单,因为其跳数限制,不适用于大型网络。 3. 工作原理: - 开启RIP发送RIP请求 - 接受请求发出响应...

ledrsnet的博客 8607

Java 并发编程(四):如何保证对象的线程安全性

本篇来谈谈 Java 并发编程:如何保证对象的线程安全性。

沉默王二 5864

Oracle EBS 寄销/VMI(1)-->基础设置

名词解释:     BPA:一揽子采购协议     BPR:一揽子发放     SR:来源补充规则     ASL:合格供应商列表     VMI: 供应商管理库存     寄售(Consignment)是把货物放在客户那里,客户要用的时候才付款的库存,一般是指卖方把

陈显平的专栏-Oracle EBS 1万+

一、Java并发编程实战之线程安全性

文章目录Java并发编程实战之基础知识一、线程安全性1、定义2、无状态对象和有状态对象二、原子性1、竞态条件2、延迟初始化中的竞态条件3、复合操作(如AtomicLong)三、加锁机制1、加锁问题的引出2、内置锁3、重入锁重入锁的一种实现方法4、用锁来保护状态5、活跃性和性能 Java并发编程实战之基础知识 要编写线程安全的代码,其核心在于要对状态访问操作进行管理,特别是共享的和可变的状态的访问。...

weixin_38367817的博客 562

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

《Java并发编程实战》笔记——线程安全性

《Java并发编程实战》笔记 基础知识 线程安全性 如果当多个线程访问同一个可变的状态参量时没有使用合适的同步,那么程序就会出现错误。 有三种方式可以修复这个问题: 不在线程之间共享该变状态变量 将状态变量修改为不可变的变量 在访问状态变量时使用同步 当设计线程安全的类时,良好的面向对象技术、不可修改性,以及明晰的不变性规范都能起到一定的帮助作用。 编写并发程序应用时,一种正确的编程方法:首先使代码正确运行,然后再提高代码的速度。即便如此,最好也只是当性能测试结果和应用需求告诉你必须提高性能,以及测量结

fromscratchx的博客 251

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

《java并发编程实战》 第一 二章 线程安全性

《java并发编程实战笔记》第一章 简介提到的概念内容笔记合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入 第一章 简介 注册C...

乌鸦大大 912

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

Java并发编程 线程安全性

Java并发编程 线程安全性 “共享”意味着变量可以由多个线程同时访问,而“可变”则意味着变量的值在生命周期内可以发生变化。如何防止在数据上发生不受控的并发访问,是我们需要讨论的点。 一个对象是否需要是线程安全的,取决于它是否被多个线程访问。这指的是在程序中访问对象的方式,而不是对象要实现的功能。要使得对象是线程安全的,需要采用同步机制来协同对对象可变状态的访问。如果无法实现协同,那么可能会导致数据破坏以及其他不该出现的结果。 当多个线程访问某个状态变量并且其中有一个线程执行写入操作时,必须采用同步机制来协

liam12138的博客 229

Java并发编程实战(学习笔记 一 第二章 线程安全性)

线程安全性(Thread Safety)

ahaha413525642的博客 1759

并发编程基础一----并发安全性

什么是并发? 并发是在同一个时间段内,同时可以处理多个事件,从而提高资源的利用率;并通过不同粒度的时间分片以保证通过用户和程序在不同的时间段内可以享有计算机上的资源的使用权;并使每个程序只执行一个任务,且只在必要时进行通信,从而简化编程。 多线程的优势 多线程可以有效的降低程序的开发和维护成本 ...

sky_dianziaihaozhe的博客 435

C++ 之 并发编程基础

C++并发编程基础 在C++线程库中提供一个native_handle()成员函数,允许通过使用平台相关API直接操作底层实现。 为了并发地运行函数,需要使用特定的函数以及对象来管理各个线程。C++在头文件中提供了管理线程的类和函数 一个简单的Hello, Concurrent World程序: #include <iostream> #include <thread> ...

士杰的博客 1004

Java并发-线程安全性

线程安全性在脱离锁与同步机制后很难保证,从抽象的层面来讲,掌握上述概念对于后面更多多线程编程的知识学习很有必要,因为多线程编程本质上是一种访问安全思想的体现。接下来的文章会涉及到Java编程语言中具体的某种锁或者同步机制,有了抽象层面的基础,学习具象知识也就变得不难了。

qq_70219364的博客 1179

《java并发编程实战》随笔——第二章 线程安全性

这是《java并发编程实战》第一部分基础知识的第一章。 线程安全性 书中balabala了一堆,其实线程安全性最核心的就是正确性,正确性的含义就是,类的行为与其规范完全一致,通常我们说某个类是线程安全的其实就是说: 当多个线程访问这个类时,这个类始终都能表现出正确的行为。 举个例子,现在要模拟实现一个c++的vector类,它有一个方法push_back(const T &item), 表

jiage123的博客 471

java并发编程实战系列-线程安全性

1 多线程的优势及风险 1.1 线程的优势 发挥多处理器的强大功能。 建模的简单性。通过使用线程,可以将复杂并且异步的工作流进一步分解为简单并且同步的工作流,每个工作流在一个单独的线程中运行,并在特定的同步位置进行交互。我们可以通过现有的框架来实现上述目标,例如Servlet。该框架负责一些细节问题,将请求管理、线程创建和负载平衡等在正确的时刻将请求分发给正确的应用程序组件,开发人员并不需要了解...

sunjian1122的博客 214

并发编程 · 基础篇 · android线程那些事

小木箱成长营并发编程系列教程(排期中): 并发编程 · 基础篇(下) · android线程池那些事 并发编程 · 提高篇(上) · Java并发关键字那些事 并发编程 · 提高篇(下) · Java锁安全性那些事 并发编程 · 高级篇(上) · Java内存模型那些事 并发编程 · 高级篇(下) · Java并发BATJ面试之谈 并发编程 · 实战篇 · android下载器实现 Tips: 关注微信公众号小木箱成长营,回复 "并发编程" 可免费获得并发编程思维导图 一、序言 Hello,我是小木

Kibaco的博客 713

了解和熟悉多线程(三)《Java并发编程中的线程安全性》

然而,并发编程会引入一系列的线程安全性问题,如果不加以正确处理,可能导致程序出现严重的错误和异常。本文将深入探讨Java中的线程安全性问题及其原因,介绍常见的线程安全性工具和技术,如锁、原子类等,并提供编写线程安全代码的最佳实践和方法。这会导致数据不一致性,程序可能会读取到错误的数据。例如,一个线程正在修改一个共享变量,而另一个线程正在读取它,可能会读取到未完全更新的值。通过以上示例和介绍,我们可以更全面地了解Java中的线程安全性问题、常见的线程安全性工具和技术,以及编写线程安全代码的最佳实践和方法。

weixin_42279822的博客 893

JAVA并发编程-线程安全性

线程安全性:当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程如何交替执行,并且在主调代码中不需要任何额外的同步或协同,各个类都能表现出正确的行为,那么称这个类是线程安全的。 1.原子性:提供了互斥访问,同一时刻只能有一个线程对它进行操作 使用int类型,共5000个线程,每次允许200个线程同时进行。不能确保每次结果都为5000。 package com.fukexin....

kelett551的博客 275
上一篇: 【Java基础】ConcurrentHashMap原理分析及应用
下一篇: 【Mybatis】项目开发中模糊查询LIKE的四种实现及注意点
少侠露飞
博客等级 码龄9年 214粉丝 117原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值