【多线程】-- 09 线程同步之三大不安全案例举例

news2024/12/23 10:14:30

多线程

6 线程同步

多个线程操作同一个资源

处理多线程问题时,多个线程访问同一个对象,并且某些线程还想修改这个对象,这时候就需要线程同步。线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面的线程使用完毕,下一个线程再使用。

  • 并发:同一个对象多个线程同时操作

  • 队列和

由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突问题,为了保证数据在方法中被访问时的正确性,在访问时加入锁机制synchronized,当一个线程获得对象的排它锁,独占资源,其他线程必须等待,使用后释放锁即可。存在以下问题:

  1. 一个线程持有锁会导致其他所有需要此锁的线程挂起;
  2. 在多线程竞争下,加锁、释放锁会导致比较多的上下文切换和调度延时,引起性能问题;
  3. 如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置,引起性能问题。

【三大不安全案例】

  • 买票案例
package com.duo.synchronize;

//不安全案例1:买票
public class UnsafeCase1 {

    public static void main(String[] args) {
        BuyTicket station = new BuyTicket();

        new Thread(station, "Hua").start();
        new Thread(station, "Ming").start();
        new Thread(station, "Hong").start();
    }
}

class BuyTicket implements Runnable {

    private int ticketNum = 10;
    boolean flag = true;

    @Override
    public void run() {
        while (flag) {
            buy();
        }
    }

    private void buy() {
        if (ticketNum <= 0) {
            flag = false;
            return;
        }

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(Thread.currentThread().getName() + "抢到了第" + ticketNum-- + "张票");
    }
}

运行结果:

图1

可以发现,由于线程不安全(没有让三个线程“排队”买票),会出现同时抢到同一张票的情况。而且也可能会出现买到编号为-1的票:

图2

  • 银行取钱案例
package com.duo.synchronize;

//银行两人同时取钱
public class UnsafeCase2 {

    public static void main(String[] args) {
        Account account = new Account(1_000_000, "基金");

        Withdraw husband = new Withdraw(account, 500_000, "丈夫");
        Withdraw wife = new Withdraw(account, 1_000_000, "妻子");

        husband.start();
        wife.start();
    }
}

class Account {
    int balance;
    String name;

    public Account(int balance, String name) {
        this.balance = balance;
        this.name = name;
    }
}

class Withdraw extends Thread {

    Account account;  //账户
    int transaction;  //交易金额
    int change;  //现在手中的零钱总金额

    public Withdraw(Account account, int transaction, String name) {
        super(name);
        this.account = account;
        this.transaction = transaction;
    }

    //取钱
    @Override
    public void run() {
        if (account.balance - transaction < 0) {
            System.out.println(Thread.currentThread().getName() + "账户余额不足,交易失败");
            return;
        }

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        account.balance -= transaction;
        change += transaction;

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //Thread.currentThread().getName() == this.getName()
        System.out.println(this.getName() + "手中当前金额:" + change);
        System.out.println(account.name + "账户当前余额为:" + account.balance);

    }
}

运行结果:

图3

可以看出,”基金“账户中原本总共只有1000000,丈夫与妻子都在此账户中取钱,前者取了500000,后者取了1000000,按理来说,账户当前余额应不足以两人同时取出并输出"账户余额不足,交易失败",但运行结果显示基金账户当前余额为-500000,这是由于丈夫取完500000之后账户余额为500000,在此之前妻子线程启动时经过首步判断账户当前余额满足交易条件,故代码会继续向下运行并执行取钱操作,所以500000-1000000=-500000.

此外,代码中额外添加的sleep()放大了此问题的发生性

  • 线程不安全的集合
package com.duo.synchronize;

import java.util.ArrayList;
import java.util.List;

//不安全案例3:线程不安全的集合
public class UnsafeCase3 {

    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        for (int i = 0; i < 5000; i++) {
            new Thread(() -> {
                list.add(Thread.currentThread().getName());
            }).start();
        }

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(list.size());
    }
}

运行结果:

图4

可以看到,最终输出的list集合大小并非5000。这是因为存在两个线程操作了集合同一位置的情况,故而集合总的元素个数就会减少。


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

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

相关文章

1.0 十大经典排序算法

分类 算法 本系列算法整理自&#xff1a;https://github.com/hustcc/JS-Sorting-Algorithm 同时也参考了维基百科做了一些补充。 排序算法是《数据结构与算法》中最基本的算法之一。 排序算法可以分为内部排序和外部排序&#xff0c;内部排序是数据记录在内存中进行排序&#…

vue3使用vue-router路由(路由懒加载、路由传参)

vue-router 是 vue的一个插件库 1. 专门用来实现一个SPA单页面应用 2 .基于vue的项目基本都会用到此库 SPA的理解 1) 单页Web应用&#xff08;single page web application&#xff0c;SPA&#xff09; 2) 整个应用只有一个完整的页面 3) 点击页面中的链接不会刷新页面, 本…

2023年12月3日支付宝蚂蚁庄园小课堂今日答案是什么?

问题&#xff1a;雪天行车&#xff0c;路面会有不少前车行驶的轨迹&#xff0c;最好&#xff1f; 答案&#xff1a;顺着前车轨迹行驶 解析&#xff1a;雪天路面湿滑&#xff0c;而且可能有冰雪等堆积物遮盖路面&#xff0c;所以&#xff0c;最好顺着前车轨迹减速慢行&#xf…

网络和Linux网络_8(传输层)TCP协议_续(流量控制+滑动窗口+拥塞控制+紧急指针+listen第二个参数)

目录 1. 流量控制 2. 滑动窗口 2.1 滑动窗口概念 2.2 滑动窗口模型详解 高速重发控制&#xff08;快重传&#xff09; 3. 拥塞控制和拥塞窗口 4. 延迟应答 5. 捎带应答 6. 面向字节流 7. 粘包问题 8. 16位紧急指针 9. listen的第二个参数 10. TCP总结异常情况与UD…

【开箱即用】前后端同时开源!周末和AI用Go语言共同研发了一款笔记留言小程序!

大家好&#xff0c;我是豆小匠。 真的是当你在怀疑AI会不会取代人类的时候&#xff0c;别人已经用AI工具加速几倍的生产速度了… 周末体验了和AI共同开发的感受&#xff0c;小项目真的可以一人全干了… 本次实验使用的AI工具有两个&#xff1a;1. GitHub Copilot&#xff08;…

怎么通过邻接矩阵求图的通路总数和回路总数?

通路总数 邻接矩阵所有元素的之和 回路总数 邻接矩阵对角线元素之和

Unittest单元测试之unittest用例执行顺序

unittest用例执行顺序 当在一个测试类或多个测试模块下&#xff0c;用例数量较多时&#xff0c;unittest在执行用例 &#xff08;test_xxx&#xff09;时&#xff0c;并不是按从上到下的顺序执行&#xff0c;有特定的顺序。 unittest框架默认根据ACSII码的顺序加载测试用例&a…

有文件实体的后门无文件实体的后门rootkit后门

有文件实体后门和无文件实体后门&RootKit后门 什么是有文件的实体后门&#xff1a; 在传统的webshell当中&#xff0c;后门代码都是可以精确定位到某一个文件上去的&#xff0c;你可以rm删除它&#xff0c;可以鼠标右键操作它&#xff0c;它是有一个文件实体对象存在的。…

ClassCMS2.4漏洞复现

CMS源码在附件中 环境搭建 使用phpstudy2016搭建web环境&#xff0c;php版本为5.5 安装CMS 这里选择Mysql数据库进行安装 用户名和密码都写默认的admin方便记忆 输入完成后点击安装 点击安装 CMS的安装过程中有个报错忽略就好&#xff0c;登录不进后台的话刷新一下页面 进入了C…

1688API接口系列,1688开放平台接口使用方案(商品详情数据+搜索商品列表+商家订单类)

1688商品详情接口是指1688平台提供的API接口&#xff0c;用于获取商品详情信息。通过该接口&#xff0c;您可以获取到商品的详细信息&#xff0c;包括商品标题、价格、库存、描述、图片等。 要使用1688商品详情接口&#xff0c;您需要先申请1688的API权限&#xff0c;并获取ac…

LeetCode - 965. 单值二叉树(C语言,二叉树,配图)

二叉树每个节点都具有相同的值&#xff0c;我们就可以比较每个树的根节点与左右两个孩子节点的值是否相同&#xff0c;如果不同返回false&#xff0c;否则&#xff0c;返回true。 如果是叶子节点&#xff0c;不存在还孩子节点&#xff0c;则这个叶子节点为根的树是单值二叉树。…

Course2-Week1-神经网络

Course2-Week1-神经网络 文章目录 Course2-Week1-神经网络1. 神经网络概述1.1 欢迎来到Course21.2 神经元和大脑1.3 引入神经网络-需求预测1.4 神经网络的其他示例-图像感知 2. 神经网络的数学表达式2.1 单层的神经网络-需求预测2.3 前向传播的神经网络-手写数字识别 3. Tensor…

蓝桥杯每日一题2023.12.2

题目描述 蓝桥杯大赛历届真题 - C 语言 B 组 - 蓝桥云课 (lanqiao.cn) 题目分析 答案&#xff1a;3598180 由题目分析可以知道&#xff0c;给小明发的牌一共有13种类型&#xff0c;每种类型的牌一共有四张。对于每种牌&#xff0c;我们都有5种选择&#xff0c;不拿、拿一张、…

文字、图片免费生成视频和专属数字人,你不来试试吗?

查看生成的效果&#xff1a;AI产生的视频&#xff08;关注公众号&#xff0c;获取精彩内容&#xff09; 您是否想要制作一些令人惊叹的视频&#xff0c;但又没有视频编辑的技能或经验&#xff1f;您是否想要利用人工智能的力量&#xff0c;让您的图片和声音变成动态的视频&…

Java研学-IO流(三)

六 字节流 – 字节输出流系列 OutPutStream体系 1 OutPutStream系列 – 字节输出流 // 表示字节输出流所有类的超类&#xff0c;输出流接受输出字节并将其发送到某个接收器 public abstract class OutputStreamFileOutputStream/BufferedOutputStream 2 FileOutputStream类设…

操作系统-文件管理

文件的属性 文件名&#xff1a;由创建文件的用户决定文件名&#xff0c;主要说为了方便用户找到文件&#xff0c;同一个目录下不允许有重名文件。 标识符&#xff1a;一个系统内的各文件标识符唯一&#xff0c;对用户来说毫无可读性&#xff0c;因此标识符只是操作系统用于区分…

Linux删除了大文件为什么磁盘空间没有释放?

某天&#xff0c;收到监控系统的告警信息&#xff0c;说磁盘空间占用过高&#xff0c;登上服务器&#xff0c;使用 df -h 一看&#xff0c;发现磁盘占用率已经 96%了&#xff1a; 通过查看 /usr/local/nginx/conf/vhost/xxx.conf 找到 access_log 和 error_log 的路径&#x…

【每日一题】找出叠涂元素

文章目录 Tag题目来源题目解读解题思路方法一&#xff1a;哈希表 写在最后 Tag 【哈希表】【数组】【2023-12-01】 题目来源 2661. 找出叠涂元素 题目解读 从左往右遍历 arr 给矩阵 mat 上色&#xff0c;在上色的过程中矩阵的某一行或者某一列的全部被上色了&#xff0c;返回…

Java中实现HTTPS连接的最佳实践

引言 大家好&#xff01;我是小黑。今天咱们来聊聊一个既热门又实用的话题&#xff1a;在Java中如何实现HTTPS连接。现在的网络世界&#xff0c;安全性是大家都非常关注的问题&#xff0c;特别是对于咱们这些程序员来说&#xff0c;更是如此。想想看&#xff0c;如果你的网站或…

AI创作ChatGPT源码+AI绘画(Midjourney绘画)+DALL-E3文生图+思维导图生成

一、AI创作系统 SparkAi创作系统是基于ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统&#xff0c;支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI…