广告
返回顶部
首页 > 资讯 > 后端开发 > Python >Java多线程之线程安全问题详解
  • 688
分享到

Java多线程之线程安全问题详解

2024-04-02 19:04:59 688人浏览 泡泡鱼

Python 官方文档:入门教程 => 点击学习

摘要

目录1. 什么是线程安全和线程不安全?2. 自增运算为什么不是线程安全的?3. 临界区资源和竞态条件总结:面试题: 什么是线程安全和线程不安全?自增运算是不是线程安全的?如何保证多线

面试题:

  • 什么是线程安全和线程不安全?
  • 自增运算是不是线程安全的?如何保证多线程下 i++ 结果正确?

1. 什么是线程安全和线程不安全?

什么是线程安全呢?当多个线程并发访问某个Java对象时,无论系统如何调度这些线程,也无论这些线程将如何交替操作,这个对象都能表现出一致的、正确的行为,那么对这个对象的操作是线程安全的。

如果这个对象表现出不一致的、错误的行为,那么对这个对象的操作不是线程安全的,发生了线程的安全问题。

2. 自增运算为什么不是线程安全的?

线程安全实验:两个线程对初始值为 0 的静态变量一个做自增,一个做自减,各做 5000 次,结果是 0 吗?具体的代码如下

public class ThreadDemo {
    private static int i = 0;
    public static void main(String[] args) throws InterruptedException {
        // 线程1对变量i做5000次自增运算
         Thread t1 = new Thread(()->{
             for(int j=0;j<5000;j++){
                 i++;
             }
         });
         Thread t2 = new Thread(()->{
             for(int j=0;j<5000;j++){
                 i--;
             }
         });
         t1.start();
         t2.start();
         // 主线程等待t1线程和t2线程执行结束再继续执行
         t1.join();
         t2.join();
        System.out.println(i);// 581 / -1830 / 0
    }
}

以上的结果可能是正数、负数、零。为什么呢?因为 Java 中对静态变量的自增,自减并不是原子操作,要彻底理解,必须从字节码来进行分析。

例如对于 i++ 而言,实际会产生如下的 JVM 字节码指令:

getstatic i  // 获取静态变量i的值
iconst_1     // 准备常量1
iadd         // 自增
putstatic i  // 将修改后的值存入静态变量i

而对应 i-- 也是类似:

getstatic i  // 获取静态变量i的值
iconst_1     // 准备常量1
isub         // 自减
putstatic i  // 将修改后的值存入静态变量

而 Java 的内存模型如下,完成静态变量的自增,自减需要在主存和工作内存中进行数据交换:

在这里插入图片描述

如果是单线程以上 8 行代码是顺序执行(不会交错)没有问题:

在这里插入图片描述

但多线程下这 8 行代码可能交错运行:

出现负数的情况:

在这里插入图片描述

出现正数的情况:

在这里插入图片描述

因此,一个自增运算符是一个复合操作,至少包括三个JVM指令:“内存取值”“寄存器增加1”和“存值到内存”。这三个指令在JVM内部是独立进行的,中间完全可能会出现多个线程并发进行。“内存取值”“寄存器增加1”和“存值到内存”这三个JVM指令本身是不可再分的,它们都具备原子性,是线程安全的,也叫原子操作。但是,两个或者两个以上的原子操作合在一起进行操作就不再具备原子性了。比如先读后写,就有可能在读之后,其实这个变量被修改了,出现读和写数据不一致的情况。

3. 临界区资源和竞态条件

在多个线程操作相同资源(如变量、数组或者对象)时就可能出现线程安全问题。一般来说,只在多个线程对这个资源进行写操作的时候才会出现问题,如果是简单的读操作,不改变资源的话,显然是不会出现问题的。

临界区资源表示一种可以被多个线程使用的公共资源或共享数据,但是每一次只能有一个线程使用它。一旦临界区资源被占用,想使用该资源的其他线程则必须等待。在并发情况下,临界区资源是受保护的对象。

临界区代码段是每个线程中访问临界资源的那段代码,多个线程必须互斥地对临界区资源进行访问。线程进入临界区代码段之前,必须在进入区申请资源,申请成功之后执行临界区代码段,执行完成之后释放资源。临界区代码段的进入和退出如图所示:

在这里插入图片描述

竞态条件可能是由于在访问临界区代码段时没有互斥地访问而导致的特殊情况。如果多个线程在临界区代码段的并发执行结果可能因为代码的执行顺序不同而不同,我们就说这时在临界区出现了竞态条件问题。

比如下面代码中的临界区资源和临界区代码段:

public class SafeDemo {
    // 临界区资源
    private static int i = 0;
    // 临界区代码段
    public void selfIncrement(){
        for(int j=0;j<5000;j++){
            i++;
        }
    }
    // 临界区代码段
    public void selfDecrement(){
        for(int j=0;j<5000;j++){
            i--;
        }
    }
	// 这个不是临界区代码,因为虽然使用了共享资源,但是这个方法并没有被多个线程同时访问
    public int getI(){
        return i;
    }
}
public class ThreadDemo {
    public static void main(String[] args) throws InterruptedException {
        SafeDemo safeDemo = new SafeDemo();
        Thread t1 = new Thread(()->{
            safeDemo.selfIncrement();
        });
        Thread t2 = new Thread(()->{
            safeDemo.selfDecrement();
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(safeDemo.getI());
    }
}

当多个线程访问临界区的selfIncrement()方法时,就会出现竞态条件的问题。更标准地说,当两个或多个线程竞争同一个资源时,对资源的访问顺序就变得非常关键。为了避免竞态条件的问题,我们必须保证临界区代码段操作具备排他性。这就意味着当一个线程进入临界区代码段执行时,其他线程不能进入临界区代码段执行。

总结:

(1) 一个程序运行多个线程本身是没有问题的,问题出在多个线程访问共享资源,多个线程读共享资源其实也没有问题,而在多个线程对共享资源读写操作时发生指令交错,就会出现问题 ;

(2) 一段代码块内如果存在对共享资源的多线程读写操作,称这段代码块为临界区代码块;

(3) 多个线程在临界区内执行,由于代码的执行序列不同而导致结果无法预测,称之为发生了竞态条件;

在Java中,可以使用synchronized关键字,使用Lock显式实例,或者使用原子变量(AtomicVariables)对临界区代码段进行排他性保护。

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注编程网的更多内容!      

--结束END--

本文标题: Java多线程之线程安全问题详解

本文链接: https://www.lsjlt.com/news/141102.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Java多线程之线程安全问题详解
    目录1. 什么是线程安全和线程不安全?2. 自增运算为什么不是线程安全的?3. 临界区资源和竞态条件总结:面试题: 什么是线程安全和线程不安全?自增运算是不是线程安全的?如何保证多线...
    99+
    2022-11-13
  • Java多线程之线程安全问题详情
    目录1.线程安全概述1.1什么是线程安全问题1.2一个存在线程安全问题的程序2.线程加锁与线程不安全的原因2.1案例分析2.2线程加锁2.2.1什么是加锁2.2.2如何加锁2.2.3...
    99+
    2022-11-13
  • Java多线程之线程安全问题
    文章目录 一. 线程安全概述1. 什么是线程安全问题2. 一个存在线程安全问题的程序 二. 线程不安全的原因和线程加锁1. 案例分析2. 线程加锁2.1 理解加锁2.2 synchroni...
    99+
    2023-09-21
    java 线程安全 多线程 synchronized jvm
  • Java多线程之线程安全问题怎么解决
    本篇内容主要讲解“Java多线程之线程安全问题怎么解决”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java多线程之线程安全问题怎么解决”吧!1.线程安全概述1.1什么是线程安全问题首先我们需要...
    99+
    2023-06-30
  • Java多线程 - 线程安全和线程同步解决线程安全问题
    文章目录 线程安全问题线程同步方式一: 同步代码块方式二: 同步方法方式三: Lock锁 线程安全问题 线程安全问题指的是: 多个线程同时操作同一个共享资源的时候可能会出现业务安全问题,称为线程安全问题。 举例:...
    99+
    2023-08-20
    java 安全 jvm
  • Java使用线程同步解决线程安全问题详解
    第一种方法:同步代码块: 作用:把出现线程安全的核心代码上锁 原理:每次只能一个线程进入,执行完毕后自行解锁,其他线程才能进来执行 锁对象要求:理论上,锁对象只要对于当前同时执行的线...
    99+
    2022-11-13
  • java多线程创建及线程安全详解
    什么是线程 线程被称为轻量级进程,是程序执行的最小单位,它是指在程序执行过程中,能够执行代码的一个执行单位。每个程序程序都至少有一个线程,也即是程序本身。 线程...
    99+
    2022-11-12
  • 关于java中线程安全问题详解
    目录一、什么时候数据在多线程并发的环境下会存在安全问题?二、怎么解决线程安全问题?三、银行 取钱/存钱 案例为什么会出现线程安全问题四、总结 一、什么时候数据在多线程并发的环境下会存...
    99+
    2022-11-12
  • Java多线程下解决数据安全问题
    目录同步代码块同步方法lock锁同步代码块 基本语句 synchronized (任意对象) { 操作共享代码 } 代码示例 public class SellTicket ...
    99+
    2022-11-12
  • Java多线程之线程状态详解
    目录 线程状态停止线程线程休眠模拟网络延迟(放大问题的发生性)模拟计时线程礼让插队(线程强制执行)线程状态观测线程优先级守护线程总结 线程状态 五个状态:新生、就...
    99+
    2022-11-12
  • Java中线程状态+线程安全问题+synchronized的用法详解
    目录java中的线程状态线程安全问题案例分析多线程对同一变量进行写操作内存可见性问题指令重排序问题synchronized的用法synchronized起作用的本质修饰普通方法修饰静...
    99+
    2022-11-13
  • Java多线程之哲学家就餐问题详解
    目录一、题目二、题目解析三、代码实现四、运行效果截图五、结语一、题目 教材提供一个哲学家就餐问题的解决方案的框架。本问题要求通过pthreads 互斥锁来实现这个解决方案。 哲学家...
    99+
    2022-11-12
  • Java多线程中线程安全问题的示例分析
    这篇文章主要介绍了Java多线程中线程安全问题的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。1. 什么是线程安全和线程不安全?什么是线程安全呢?当多个线程并发访问某...
    99+
    2023-06-29
  • 【Java系列】详解多线程(三)—— 线程安全(下篇)
    个人主页:兜里有颗棉花糖 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 兜里有颗棉花糖 原创 收录于专栏【Java系列专栏】【JaveEE学习专栏】 本专栏旨在分享学习Java的一点学习心得,欢迎大家在评...
    99+
    2023-12-22
    java 安全 多线程 java-ee
  • Java中线程安全问题
    目录一.线程不安全二.那些情况导致了线程不安全?三.Java中解决线程不安全的方案1.volatile“轻量级”解决线程不安全2.synchronized自动加锁四.公平锁与非公平锁...
    99+
    2022-11-12
  • java多线程之线程安全(重点,难点)
    线程安全 1. 线程不安全的原因:1.1 抢占式执行1.2 多个线程修改同一个变量1.3 修改操作不是原子的锁(synchronized)1.一个锁对应一个锁对象.2.多个锁对应一个锁对象.2.多个锁对应多个锁对象.4. 找出代码错...
    99+
    2023-08-17
    java jvm 开发语言
  • Java多线程之Interrupt中断线程详解
    目录一、测试代码二、测试三、执行过程描述四、输出日志五、结论六、主要方法释义七、DEMO八、拓展程序九、实战一、测试代码 https://gitee.com/zture/sprin...
    99+
    2022-11-12
  • SimpleDateFormat线程安全问题排查详解
    目录一. 问题现象二. 原因排查三. 原因分析四. 解决方案一. 问题现象 运营部门反馈使用小程序配置的拉新现金红包活动二维码,在扫码后跳转至404页面。 二. 原因排查 首先,检查...
    99+
    2022-11-13
    SimpleDateFormat线程安全排查 SimpleDateFormat线程安全
  • 聊聊java多线程创建方式及线程安全问题
    什么是线程 线程被称为轻量级进程,是程序执行的最小单位,它是指在程序执行过程中,能够执行代码的一个执行单位。每个程序程序都至少有一个线程,也即是程序本身。 线程的状态 新建(New)...
    99+
    2022-11-12
  • 程序猿必须要掌握的多线程安全问题之锁策略详解
    目录一、常见的锁策略1.1 乐观锁1.2 悲观锁1.3 读写锁1.4 公平锁与非公平锁1.5 自旋锁(Spin Lock)1.6 可重入锁1.7 相关题目二、CAS问题2.1 什么是...
    99+
    2022-11-12
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作