4.4 变量的线程安全分析

news2025/2/26 11:43:32

目录

    • 4.4 变量的线程安全分析
      • 1、成员变量和静态变量是否线程安全?
      • 2、局部变量是否线程安全?
      • 3、局部变量线程安全分析
      • 4、常见线程安全类
      • 5、实例分析
    • 4.5 习题
      • 1、卖票练习
      • 2、转账练习

4.4 变量的线程安全分析

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

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

2、局部变量是否线程安全?

  • 局部变量是线程安全的
  • 局部变量引用的对象则未必
    • 如果该对象没有逃离方法的作用访问,它是线程安全的
    • 如果该对象逃离方法的作用范围,需要考虑线程安全

3、局部变量线程安全分析

1) 安全分析1 - 局部变量是基本数据类型
在这里插入图片描述

2) 安全分析1 - 局部变量是引用数据类型,且没有逃离方法的作用域

先看一个成员变量的例子:

public class TestThreadSafe {

    static final int THREAD_NUMBER = 2;
    static final int LOOP_NUMBER = 200;

    public static void main(String[] args) {
        ThreadUnsafe test = new ThreadUnsafe();
        for (int i = 0; i < THREAD_NUMBER; i++) {
            new Thread(() -> {
                test.method1(LOOP_NUMBER);
            }, "Thread" + (i+1)).start();
        }
    }
}
class ThreadUnsafe {

    ArrayList<String> list = new ArrayList<>();
    
    public void method1(int loopNumber) {
        for (int i = 0; i < loopNumber; i++) {
            method2();
            method3();
        }
    }

    private void method2() {
        list.add("1");
    }

    private void method3() {
        list.remove(0);
    }
}

在这里插入图片描述
分析:

  • 无论哪个线程中的 method2 引用的都是同一个对象中的 list 成员变量
  • method3 与 method2 分析相同
    在这里插入图片描述

将 list 修改为局部变量,那么就不会有上述问题了
list 是局部变量,每个线程调用时会创建其不同实例,没有共享

class ThreadSafe {
    public final void method1(int loopNumber) {
        ArrayList<String> list = new ArrayList<>();
        for (int i = 0; i < loopNumber; i++) {
            method2(list);
            method3(list);
        }
    }

    public void method2(ArrayList<String> list) {
        System.out.println("+1");
        list.add("1");
    }

    public void method3(ArrayList<String> list) {
        System.out.println("-1");
        list.remove(0);
    }
}

3)安全分析3 - 局部变量引用对象,且对象逃离了方法的作用范围
此时是线程不安全的

public static StringBuilder method3(){
    StringBuilder s1 = new StringBuilder();
    s1.append("A");
    s1.append("B");
    ...
    return s1;
}

4)方法访问修饰符带来的思考,如果把 method2 和 method3 的方法修改为 public 会不会代理线程安全问题?
情况1:有其它线程调用 method2 和 method3
情况2:在 情况1 的基础上,为 ThreadSafe 类添加子类,子类覆盖 method2 或 method3 方法,即

class ThreadSafeSubClass extends ThreadSafe{
    @Override
    public void method3(ArrayList<String> list) {
//        System.out.println(2);
        new Thread(() -> {
            list.remove(0);
        }).start();
    }
}

class ThreadSafe {
    public final void method1(int loopNumber) {
        ArrayList<String> list = new ArrayList<>();
        for (int i = 0; i < loopNumber; i++) {
            method2(list);
            method3(list);
        }
        System.out.println(list.size());
    }

    public void method2(ArrayList<String> list) {
//        System.out.println("+1");
        list.add("1");
    }

    public void method3(ArrayList<String> list) {
//        System.out.println("-1");
        list.remove(0);
    }
}

public class TestThreadSafe {

    static final int THREAD_NUMBER = 2;
    static final int LOOP_NUMBER = 200;

    public static void main(String[] args) {
        ThreadSafeSubClass test = new ThreadSafeSubClass();
        for (int i = 0; i < THREAD_NUMBER; i++) {
            new Thread(() -> {
                test.method1(LOOP_NUMBER);
            }, "Thread" + (i+1)).start();
        }
    }
}

在这里插入图片描述

4、常见线程安全类

String
Integer
StringBuffer
Random
Vector
Hashtable
java.util.concurrent 包下的类
这里说它们是线程安全的是指,多个线程调用它们同一个实例的某个方法时,是线程安全的。

  • 它们的每个方法是原子的
  • 但注意它们多个方法的组合不是原子的,见后面分析

5、实例分析

4.5 习题

1、卖票练习

public class ExerciseSell {
    public static void main(String[] args) throws InterruptedException {
        // 模拟多人买票
        TicketWindow window = new TicketWindow(1000);

        // 所有线程的集合
        List<Thread> threadList = new ArrayList<>();
        // 卖出的票数统计
        List<Integer> amountList = new Vector<>();
        for (int i = 0; i < 2000; i++) {
            Thread thread = new Thread(() -> {
                // 买票
                int amount = window.sell(random(5));
                // 统计买票数
                amountList.add(amount);
            });
            threadList.add(thread);
            thread.start();
        }

        //等待所有的线程运行结束
        for (Thread thread : threadList) {
            thread.join();
        }

        // 统计卖出的票数和剩余票数
        log.debug("余票:{}", window.getCount());
        log.debug("卖出的票数:{}", amountList.stream().mapToInt(i -> i).sum());
    }

    // Random 为线程安全
    static Random random = new Random();

    // 随机 1~5
    public static int random(int amount) {
        return random.nextInt(amount) + 1;
    }
}

// 售票窗口
class TicketWindow {
    private int count;

    public TicketWindow(int count) {
        this.count = count;
    }

    // 获取余票数量
    public int getCount() {
        return count;
    }

    // 售票
    /*public  synchronized int sell(int amount) {
        if (this.count >= amount) {
            this.count -= amount;
            return amount;
        } else {
            return 0;
        }
    }*/

    public int sell(int amount) {
        if (this.count >= amount) {
            this.count -= amount;
            return amount;
        } else {
            return 0;
        }
    }
}

2、转账练习

public class ExerciseTransfer {
    public static void main(String[] args) throws InterruptedException {
        Account a = new Account(1000);
        Account b = new Account(1000);
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                a.transfer(b, randomAmount());
            }
        }, "t1");
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                b.transfer(a, randomAmount());
            }
        }, "t2");
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        // 查看转账2000次后的总金额
        log.debug("total:{}", (a.getMoney() + b.getMoney()));
    }

    // Random 为线程安全
    static Random random = new Random();

    // 随机 1~100
    public static int randomAmount() {
        return random.nextInt(100) + 1;
    }
}

// 账户
class Account {
    private int money;

    public Account(int money) {
        this.money = money;
    }

    public int getMoney() {
        return money;
    }

    public void setMoney(int money) {
        this.money = money;
    }

    // 转账
    /*public void transfer(Account target, int amount) {
        if (this.money >= amount) {
            this.setMoney(this.getMoney() - amount);
            target.setMoney(target.getMoney() + amount);
        }
    }*/


    public void transfer(Account target, int amount) {
        synchronized(Account.class) {
            if (this.money >= amount) {
                this.setMoney(this.getMoney() - amount);
                target.setMoney(target.getMoney() + amount);
            }
        }
    }

    // 方法上加synchronized,锁对象是this, a向b转账使用的锁对象是Account a,
    // b向a转账使用的锁对象是Account b,两个线程使用的是不同的锁对象,所以不能控制线程安全
   /* public synchronized void transfer(Account target, int amount) {
        if (this.money > amount) {
            this.setMoney(this.getMoney() - amount);
            target.setMoney(target.getMoney() + amount);
        }
    }*/
}

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

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

相关文章

不止Chat,GPT-4 将释放更大生产力

目录 1.对 ChatGPT 的巨大超越 2.与 ChatGPT 相同的技术路线 3.GPT-4 背后的强大阵容 4.开启多模态大模型时代 相比 ChatGPT 能力有大进化&#xff0c;多模态上有突破但不多。 近日&#xff0c;多模态大模型 GPT-4 震撼登场&#xff01; GPT-4 能够接受图像和文本输入&am…

读书笔记:从ChatGPT到AIGC:智能创作与应用赋能

文章目录 引爆内容生产力应用场景&#xff1a;ChatGPT的多场景应用传媒电商影视教育金融医疗 发展梳理&#xff1a;从PGC到UGC再到AIGCPGC&#xff1a;企业和平台是内容创作的主体UGC&#xff1a;用户成为内容创作主体AIGC 内容生成&#xff1a;AIGC涵盖多样的内容模态AI图像&a…

JupyterLab 4.0 发布了

JupyterLab 是 Jupyter Notebook 的下一代版本&#xff0c;它提供了更强大的功能和更灵活的用户界面&#xff0c;6月6日&#xff0c;官方发布了JupyterLab 4.0的说明&#xff0c;并且说该版本是下一个主要的版本。 JupyterLab的主要改进是: 用户界面&#xff1a;Jupyter Note…

2023年高性能计算就业及实习岗位大爆料

关于高性能计算能做什么&#xff1f;未来的就业前景如何&#xff1f;薪资如何&#xff1f; 只要是计算密集型的软件&#xff0c;必然需要HPC工程师。最近火热的行业和技术领域&#xff0c;包括深度学习计算机视觉&#xff0c;自然语言处理&#xff0c;自动驾驶&#xff0c;Al f…

如何建立制造业设备巡检系统?零代码工具起关键性作用

什么设备巡检系统 设备巡检系统是一种管理工具&#xff0c;用于监测和记录设备运行状况&#xff0c;并在需要时指示工作人员进行巡检、维护或维修。这些系统通常由计算机软件和一组传感器或控制器组成&#xff0c;可监测各种设备的运行状况&#xff0c;如机器、设备、管线和车…

vue3-实战-06-管理后台-品牌管理模块开发

目录 1-品牌列表 1.1-需求图 1.2-定义接口和数据类型 1.3-请求接口和渲染数据 2-新增和修改品牌 2.1-需求原型分析 2.2-dialog开发 2.3-请求接口封装 2.4-图片上传组件开发 2.5-新增-修改品牌信息 3-删除品牌 4-表单校验 在开发品牌管理之前&#xff0c;我们先将mo…

【数据分享】1929-2022年全球站点的逐年最低气温(Shp\Excel\12000个站点)

气象数据是在各项研究中都经常使用的数据&#xff0c;气象指标包括气温、风速、降水、湿度等指标&#xff0c;其中又以气温指标最为常用&#xff01;说到气温数据&#xff0c;最详细的气温数据是具体到气象监测站点的气温数据&#xff01; 之前我们分享过1929-2022年全球气象站…

物联网开发的流程是怎么样的

物联网开发流程&#xff0c;物联网是指通过各种传感设备把任何物品与互联网相连接&#xff0c;进行信息交换和通信&#xff0c;以实现智能化识别、定位、跟踪、监控和管理的一种网络。物联网开发流程&#xff0c;物联网开发的步骤是怎么样的&#xff1f;可以参考以下文章内容&a…

Nautilus Chain全球行分享会,深圳站圆满举办

在北京时间 6 月 4 日&#xff0c;由 Nautilus Chain 主办的“Layer3 模块化区块链的发展探讨”为主题的全球行活动&#xff0c;在深圳&#xff08;深圳南山区清华研究院&#xff09;顺利举办&#xff0c;本次分享会联合主办方还包括 Stanford Blockchain Accelerator、Zebec P…

基于ADME的分子过滤和 lead-likeness标准

T002 基于ADME的分子过滤和 lead-likeness标准 项目来源于TeachOpenCADD 本文目标 在药物设计的背景下&#xff0c;重要的是通过例如它们的物理化学性质来过滤候选分子。 在这个教程中&#xff0c;从 ChEMBL ( Talktorial T001 )获得的化合物将按照 Lipinsik 的五法则进行…

Android AIDL Callback的使用(配源码)

零、示例说明 本示例&#xff0c;完成的功能是&#xff1a;客户端向服务端注册一个回调&#xff0c;服务端是一个商店shop&#xff0c;当商店里的产品 Product 有变化时&#xff0c;调用回调向通知客户端&#xff0c;什么商品更新了。 一、完整源代码 完整源码链接: https:/…

电脑丢失dll文件怎么一键修复?哪种dll修复方法靠谱?

在使用电脑时&#xff0c;我们经常会遇到一些问题&#xff0c;例如电脑丢失了某些dll文件。这些文件是电脑运行所必需的&#xff0c;如果缺少了这些文件&#xff0c;电脑就无法正常运行。在这种情况下&#xff0c;我们需要使用使用一些方法去进行相关的修复&#xff01;下面我们…

微信小程序原生开发功能合集十九:大屏适配、分栏代码可视化编辑、骨架屏生成及WebView使用介绍

本章实现小程序大屏适配实现、pc端分栏展示实现、代码可视化编辑、骨架屏生成及WebView组件使用介绍。   另外还提供小程序开发基础知识讲解课程,包括小程序开发基础知识、组件封装、常用接口组件使用及常用功能实现等内容,具体如下:    1. CSDN课程: https://edu.csd…

高精度气象模拟软件WRF:天气预报、观测气温、分析降水、模拟尺度气象、模拟水汽湿度、土地利用变化影响、土壤水分通量、分析风场、分析土壤水体植被等气象变量

查看原文>>>高精度气象模拟软件WRF(Weather Research Forecasting)技术及案例应用 气候是多个领域&#xff08;生态、水资源、风资源及碳中和等问题&#xff09;的主要驱动因素&#xff0c;合理认知气候变化有利于解释生态环境变化机理及过程&#xff0c;而了解现在、…

立刻杀掉Oracle中的会话(session)

一、需求描述 我们开发项目的过程中,涉及到连接oracle数据库的操作,正常来说我们执行完对应的sql语句后且关闭了连接,数据库的连接就会释放了;但是现实的情况是我们在查询数据库建立的会话时,发现存在大量的InAactive会话(我们需要找到原因,且将当前的会话立刻手动删除掉…

从零手写操作系统之RVOS任务同步和锁实现-07

从零手写操作系统之RVOS任务同步和锁实现-07 并发与同步临界区、锁、死锁自旋锁1.0 版本2.0 版本原子指令思路测试 3.0 版本测试 小结其他同步技术 本系列参考: 学习开发一个RISC-V上的操作系统 - 汪辰 - 2021春 整理而来&#xff0c;主要作为xv6操作系统学习的一个前置基础。 …

练习:程序切片

练习&#xff1a;程序切片 1 简介 注&#xff1a;问题3提到了conditioned slicing。 我们没有给出计算条件切片的算法&#xff0c;但你不应该要求这样的算法。 2 问题 1 对于以下每个代码片段&#xff0c;绘制程序依赖图&#xff08;没有定义顺序边&#xff09;。 基于这些&am…

什么是 Vue.js 中的 v-if 和 v-show 指令?

什么是 Vue.js 中的 v-if 和 v-show 指令&#xff1f; Vue.js 是一种用于构建交互式用户界面的渐进式框架。它采用了响应式的数据绑定机制和组件化的开发模式&#xff0c;让开发者能够更加高效地构建复杂的 Web 应用。在 Vue.js 中&#xff0c;v-if 和 v-show 是两个常用的指令…

STM32开发——电动车报警装置

1.项目简介 1.1项目需求 点击遥控器 A 按键&#xff0c;系统进入警戒模式&#xff0c;一旦检测到震动&#xff08;小偷偷车&#xff09;&#xff0c;则喇叭发出声响报警&#xff0c; 吓退小偷。 点击遥控器 B 按键&#xff0c;系统退出警戒模式&#xff0c;再怎么摇晃系统都不…

通过项目驱动的学习方法快速掌握Java编程

摘要 Java作为一种广泛应用于软件开发领域的编程语言&#xff0c;对于零基础的学习者来说&#xff0c;学习Java编程可能存在一定的难度。本文将介绍如何通过项目驱动的学习方法&#xff0c;帮助零起点的学习者快速掌握Java编程。通过以项目为核心的学习路径、结合实践和理论的…