首页 > 编程语言 >JAVA线程资源共享问题

JAVA线程资源共享问题

时间:2024-08-09 14:49:40浏览次数:10  
标签:资源共享 问题 JAVA syncThreadDemo01 sum 共享资源 线程 操作

JAVA多线程共享资源问题

场景引入

这个场景是一个典型的多线程共享资源的场景,主要目的是测试和观察多个线程对共享变量 sum 进行并发操作时是否会出现线程安全问题
场景描述

共享资源:

  • 共享变量 sum,初始值为 0。
  • 多个线程同时对 sum 进行操作,一个线程负责自增操作,另一个线程负责自减操作。

线程操作:

  • 每个线程在循环中对 sum 进行 1000 次操作。
  • 自增线程每次将 sum 增加 1,自减线程每次将 sum 减少 1。

日志记录:

  • 使用 Logger 记录每次操作的线程名称、操作类型(加法或减法)以及操作后的 sum 值。

目的

  • 测试线程共享问题:通过观察 sum 的值,测试是否存在线程安全问题。如果没有同步机制,可能会出现数据不一致的情况。
  • 验证并发操作的正确性:在没有同步机制的情况下,多个线程同时操作共享变量 sum,可能会导致数据竞争和不正确的结果。

预期结果

  • 线程安全问题:由于没有使用同步机制,多个线程同时操作 sum,可能会导致数据竞争,最终 sum 的值可能不正确。
    日志输出:日志会记录每次操作的详细信息,可以通过日志观察 sum 的变化情况。
public class syncThreadDemo01 extends Thread{

    Logger logger = LoggerFactory.getLogger(syncThreadDemo01.class);

    static Integer sum = 0;

    // 需求: 基于sum 开启多个线程,一个线程自增,一个线程减,测试是否有线程共享问题
    private Boolean isAdd;
    private String threadName;

    public syncThreadDemo01(Boolean isAdd,String threadName) {
        this.isAdd = isAdd;
        this.threadName = threadName;
    }

    @Override
    public void run() {
        for (int i = 0 ;i<1000;i++){
            if (isAdd){
                sum+=1;
            }else {
                sum-=1;
            }
            logger.info("线程:{}开始执行。。。。。。。。。。。,执行模式:{},输出结果:{}",threadName,isAdd?"加法":"减法",sum);
        }
    }
}
public class ThreadMainDemo {

    public static void main(String[] args) {
//        ThreadDemo thread = new ThreadDemo("线程1");
//        thread.start();

        // 测试 资源共享问题
        syncThreadDemo01 addThread = new syncThreadDemo01(true, "加线程");
        syncThreadDemo01 delThread = new syncThreadDemo01(false, "减线程");

        addThread.start();
        delThread.start();
    }
}

输出结果:
image

临界区

  • 一个程序运行多个线程本身是没有问题的
  • 问题出在多个线程访问共享资源
    • 多个线程读共享资源其实也没有问题
    • 在多个线程对共享资源读写操作时发生指令交错,就会出现问题
  • 一段代码块内如果存在对共享资源的多线程读写操作,称这段代码块为临界区
    例如,下面代码中的临界区
    image

synchronized

为了避免临界区的竞态条件发生,有多种手段可以达到目的。

  • 阻塞式的解决方案:synchronized,Lock
  • 非阻塞式的解决方案:原子变量

本次使用阻塞式的解决方案:synchronized,来解决上述问题,即俗称的【对象锁】,它采用互斥的方式让同一时刻至多只有一个线程能持有【对象锁】,其它线程再想获取这个【对象锁】时就会阻塞住。这样就能保证拥有锁的线程可以安全的执行临界区内的代码,不用担心线程上下文切换

image

输出结果:0

竟然说synchronizzed 是对象锁,那么我通过创建对象的方式,将对象作为形参会有什么效果,以下是静态对象和成员对象的方式
image

输出结果:不符合预期结果,产生了对资源共享的问题
分析:
由于创建线程的时候,分别创建了两次,所以在这个当中,对象锁锁的是它这个线程的本身,并没有对其他线程进行加锁。
image

竟然如此,换算成静态成员对象的方式,是否可以解决这个问题。
image
image

测试结果:符合预期,解决了多个线程 对 共享资源的问题。

变量的线程安全分析

成员变量和静态变量是否线程安全?

  • 如果它们没有共享,则线程安全
  • 如果它们被共享了,根据它们的状态是否能够改变,又分两种情况
    • 如果只有读操作,则线程安全
    • 如果有读写操作,则这段代码是临界区,需要考虑线程安全

标签:资源共享,问题,JAVA,syncThreadDemo01,sum,共享资源,线程,操作
From: https://www.cnblogs.com/zgf123/p/18350758

相关文章

  • Java - IO流
    (IO随用随创建,不用了就关流)1.字节流OutputStream(字节输出流,写出)FileOutputStreanmoff表示起始索引,len表示截取长度Windows换行符:"\r\n"Linux:"\n"Mac:"\r"续写:创建对象第二个参数写true字节打印流(特有方法会原样输出)无缓冲区,自动刷新无用InputStream(字节输......
  • 1.1javaSE初识
    JDK:JDK是JavaDevelopmentKit的缩写,意为Java语言的软件开发工具包(SDK)。它是Java编程的核心工具,为程序开发者提供了一个完整的开发环境。JRE:Java运行环境,是运行Java程序所必须的环境的集合,包含了JVM(Java虚拟机)和Java核心类库。Java开发工具:包括编译器(javac)、解释器(java)、调试......
  • Java SE核心技术——12常用类
    Java中的System类是Java标准库的一部分,它属于java.lang包,因此在使用时不需要显式地导入。这个类提供了一些与系统相关的功能,主要包括以下几个方面:标准输入输出:System.in:标准输入流,通常用于从键盘读取数据。System.out:标准输出流,用于向控制台打印信息。System.err:标准......
  • java实现用一个变量表示多个属性的状态
    1、流程启动及状态流转举例(场景:同一份数据在多个流程中流转,确定各流程自己的状态)1.发起流程核心代码/***二进制方式*@paramorgId*@paramnode*@paramcontractIds*/privatevoidstartWithPosition(StringorgId,Stringnode,List<String......
  • Java入门学习——Day02Java工具
    一、Javac1.1Java1(1996年)        Java编译器 javac 在Java1.0版本中首次发布。它将Java源代码(.java 文件)编译成中间字节码(.class 文件)。这个字节码可以在Java虚拟机(JVM)上运行,实现了Java的“编写一次,处处运行”的理念。1.2Java2(1998年)        在Java......
  • 在国产芯片上实现YOLOv5/v8图像AI识别-【2.3】RK3588上使用C++启用多线程推理更多内容
    本专栏主要是提供一种国产化图像识别的解决方案,专栏中实现了YOLOv5/v8在国产化芯片上的使用部署,并可以实现网页端实时查看。根据自己的具体需求可以直接产品化部署使用。B站配套视频:https://www.bilibili.com/video/BV1or421T74f基础背景对于国产化芯片来说,是采用NPU进......
  • JavaScript -- 总结 10 (小白)
    MouseEvent属性<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width,initial-scale=1.0"><title>Document<......
  • JAVA多线程的使用和创建的几种方式
    Thrad创建和使用创建实体类,继承Thread实现run()方法调用start()方法publicclassThreadDemoextendsThread{Loggerlogger=LoggerFactory.getLogger(ThreadDemo.class);privateStringtaskName;publicStringgetTaskName(){return......
  • 多线程系列:线程中包含子线程
    多线程系列:线程中包含子线程这里展示的多线程基本包含了常见的多线程操作:实现线程中包含子线程;计算子线程耗时展示一个函数如何通过参数传递进行多线程一个线程调用一个函数的情况代码示例importthreadingimporttimefrompprintimportpprintimportqueue#假......
  • JAVA修饰符、运算符、循环语句
    JAVA修饰符修饰符用来定义类、方法或者变量,通常放在语句的最前端一、访问修饰符1、default默认访问修饰符:在同一包内可见,不使用任何修饰符,使用对象为类、接口、变量、方法,访问级别是包级别(package-level),即只能被同一包中的其他类访问2、private私有访问修饰符:最严格的访问级......