java进阶—线程安全问题

news2025/7/29 23:51:47

线程安全问题,就涉及到一个资源共享,资源共享很好理解,就是多个线程同时操作一个资源池

就比如,快五一了,12306 购票,假设从北京到上海的 高铁票 一共只有200 张,现在3个人同时在线抢票,可以把一个人的app理解成一个线下窗口,这样在没有处理线程安全的情况下,就会出现抢到同一张票的可能性

我们用代码来简单模拟下

public class Ticket implements Runnable {

    // 三个窗口共享票200
    private Integer ticketCount = 200;

    @Override
    public void run() {
        while (true) {
            if (ticketCount > 0) {
                System.out.println(Thread.currentThread().getName() + "售出一张票,票号:"+ticketCount+"余票" + --ticketCount);
            } else {
                System.out.println("售罄");
                break;
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Ticket ticket = new Ticket();
        //窗口1
        Thread thread = new Thread(ticket);
        thread.setName("窗口1");
        //窗口2
        Thread thread2 = new Thread(ticket);
        thread2.setName("窗口2");
        //窗口3
        Thread thread3 = new Thread(ticket);
        thread3.setName("窗口3");

        thread.start();
        thread2.start();
        thread3.start();

    }

输出结果

在这里插入图片描述

可以看到,票号为200 的这张票,同时被三个人抢到,一个位置不可能三个人一起坐吧

来看线程安全会造成的另一种情况


public class Ticket implements Runnable {


    private Integer ticketCount = 200;

    @Override
    public void run() {
        while (true) {
            if (ticketCount > 0) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "售出一张票,票号:"+ticketCount+"余票" + --ticketCount);
            } else {
                System.out.println("售罄");
                break;
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Ticket ticket = new Ticket();
        //窗口1
        Thread thread = new Thread(ticket);
        thread.setName("窗口1");
        //窗口2
        Thread thread2 = new Thread(ticket);
        thread2.setName("窗口2");
        //窗口3
        Thread thread3 = new Thread(ticket);
        thread3.setName("窗口3");

        thread.start();
        thread2.start();
        thread3.start();

    }

}

在这里插入图片描述
我在售票前,让线程休眠100毫秒,我们执行看看

在这里插入图片描述
可以发现,出现了零票,负票,你要是不介意 扒车底,那你拿负票去车底,也不是不行

这里先说明一点,这里加了sleep,并不说明 加了才出现 错票的线程问题,为了演示,我们只是把概率提升了,不加也会出现

总结一下问题产生原因:当A一个线程进入程序,A并未操作完成,B线程也参与进来,操作同一个资源,就出现了线程安全问题

怎么解决线程安全问题呢?

其实在我们生活中,解决线程安全的例子随处可见

比如,你去上厕所,一个坑位一个人,你刚蹲进去,刚好另一个人也很着急,他刚好也进去了,这就出事了,解决办法,是不是给厕所上了锁,你进去,锁住,别人只能等,等你出来他才能进去

java 线程也是一样,解决线程安全问题,很简单,加锁

线程的锁,用一个关键字代表 Synchronized ,也可以叫做同步

同步有两种方式

  1. 同步代码块

synchronized(同步监视器){
   //需要同步代码
}

同步监视器:就是锁,任何一个类型的对象 ,但是这个锁就一个,多个线程共用这一个 (一定要注意)

需要同步代码:操作共享数据的代码

回过头来看我们上述买票的例子,我们给他上个锁


public class Ticket implements Runnable {


    private Integer ticketCount = 200;
    Object object =new Object();
    @Override
    public void run() {
        while (true) {
            synchronized (object) {
                if (ticketCount > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "售出一张票,票号:"+ticketCount+"余票" + --ticketCount);
                } else {
                    System.out.println("售罄");
                    break;
                }
            }

        }
    }

    public static void main(String[] args) throws InterruptedException {
        Ticket ticket = new Ticket();
        //窗口1
        Thread thread = new Thread(ticket);
        thread.setName("窗口1");
        //窗口2
        Thread thread2 = new Thread(ticket);
        thread2.setName("窗口2");
        //窗口3
        Thread thread3 = new Thread(ticket);
        thread3.setName("窗口3");

        thread.start();
        thread2.start();
        thread3.start();

    }

}

在这里插入图片描述
输出结果:

在这里插入图片描述
可以看到票数 到0 了,就算其他窗口还在执行,也是售罄状态

  1. 同步方法

同步方法:就是操作共享资源刚好是一个方法,我们可以直接在这个方法上同步

还是上述例子改造 ,我们先把方法提取出来 ,加上同步关键字

public class Ticket implements Runnable {


    private Integer ticketCount = 200;
    Object object =new Object();
    @Override
    public void run() {
        while (true) {
            sellTicket();
            if (ticketCount ==0) {
             System.out.println("售罄");
             break;
            }
        }
    }


    public  synchronized void  sellTicket() {
        if (ticketCount > 0) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "售出一张票,票号:"+ticketCount+"余票" + --ticketCount);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Ticket ticket = new Ticket();
        //窗口1
        Thread thread = new Thread(ticket);
        thread.setName("窗口1");
        //窗口2
        Thread thread2 = new Thread(ticket);
        thread2.setName("窗口2");
        //窗口3
        Thread thread3 = new Thread(ticket);
        thread3.setName("窗口3");

        thread.start();
        thread2.start();
        thread3.start();

    }

在这里插入图片描述
结果是一样的,解决了线程安全问题

同步好处:解决线程安全问题,当然也会影响效率,同步的代码里只能有一个线程执行,其他等待,相当于一个单线程

在这里插入图片描述
以上就是通过同步来解决线程安全的问题了,以后去上厕所,可以这么说,我去同步了,只有程序员才懂得梗

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

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

相关文章

【ONE·C || 程序编译简述】

总言 C语言:程序编译相关。    文章目录总言1、程序的翻译环境和运行环境1.1、简述1.2、翻译环境:程序编译与链接1.2.1、简介:程序如何从.c文件形成.exe可执行程序1.2.2、过程说明1.3、运行环境2、预处理详解2.1、预定义符号2.2、#define2.…

参考文献怎么查找,去哪里查找?一篇文章讲明白这些问题

在我们撰写论文查找参考文献时,往往不知道从哪里入手,本文小编就针对下面这三个方面给大家详细讲解下: 一、查找参考文献方法 二、参考文献资料查找网站 三、参考文献格式规范 一、查找参考文献方法: 1、知网全球最大的中文数据…

DBeaver连接mysql、oracle数据库

1. DBeaver连接mysql 1) 下载DBeaver https://dbeaver.io/download/,并安装 2) 新建数据库连接 3)选择mysql驱动程序 4)填写连接设置内容 5)点击 “编辑驱动设置”,并填写相关信息 6)选择本地…

九龙证券|朝着双向开放稳步前进――从沪深港通全面扩容看资本市场对外开放

2023年春天的资本商场,高水平双向敞开的步履益发铿锵。 伴随着沪深买卖所互联互通股票标的规划扩展规矩正式对外发布,3月13日,内地与香港资本商场行将迎来史上最大规划双向扩容——沪深股通标的股票合计将添加1034只,调整后沪股通…

Web前端学习:六 -- 练习小总结

1、背景颜色透明度写法: background:rgba(R,G,B,Alpha透明度) 透明度范围:0–1,1完全不透明,0完全透明 2、伪类 hovar: 当鼠标接触该元素是,显示另一种样…

安恒信息java实习面经

目录1.Java ME、EE、SE的区别,Java EE相对于SE多了哪些东西?2.jdk与jre的区别3.说一下java的一些命令,怎么运行一个jar包4.简单说一下java数据类型及使用场景5.Map跟Collection有几种实现?6.面向对象的特性7.重载和重写的区别8.重…

ElasticSearch 在Java中的各种实现

ES JavaAPI的相关体系: 词条查询 所谓词条查询,也就是ES不会对查询条件进行分词处理,只有当词条和查询字符串完全匹配时,才会被查询到。 等值查询-term 等值查询,即筛选出一个字段等于特定值的所有记录。 【SQL】 s…

工业物联网“杀手级”应用—预测性维护

一、预测性维护的必要性 随着新一轮科技革命和产业变革的兴起,工业物联网、大数据、人工智能等技术正与经济社会各领域加速渗透融合。由于市场竞争对精细化成本管控的要求,设备的重要性越来越凸显,设备的维护对策也必然从响应式维护&#xf…

[Java·算法·中等]LeetCode31. 下一个排列

每天一题,防止痴呆题目示例分析思路1题解1分析思路2题解2👉️ 力扣原文 题目 整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。 例如,arr [1,2,3] ,以下这些都可以视作 arr 的排列:[1,2,3]、[1,3,2]、…

LearnOpenGL-入门-9.摄像机

本人刚学OpenGL不久且自学,文中定有代码、术语等错误,欢迎指正。 此篇有点难理解,但是学完会对FPS第一人称3D摄像机的实现有深刻的理解 我写的项目地址:https://github.com/liujianjie/LearnOpenGLProject LearnOpenGL中文官网&a…

一、Java类加载机制

文章目录什么是类的加载?类的生命周期加载连接[验证、准备、解析]初始化结束生命周期类加载器类加载方式双亲委派机制自定义类加载器总结--类加载什么是类的加载? 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时…

Git的SSH密钥配置

Git的SSH密钥配置简记Githttps和ssh的区别基本需求SSH密钥类型ED25519 SSH 密钥RSA SSH 密钥查看您是否有现有的 SSH 密钥对设置流程设置user name和emailssh密钥配置检查是否存在ssh Key创建新的ssh key将ssh密钥添加到您的Git帐户验证您是否可以连接使用Git有一段时间了&…

代码随想录-51-110.平衡二叉树

目录前言题目1.求高度和深度的区别节点的高度节点的深度2. 本题思路分析:3. 算法实现4. pop函数的算法复杂度5. 算法坑点前言 在本科毕设结束后,我开始刷卡哥的“代码随想录”,每天一节。自己的总结笔记均会放在“算法刷题-代码随想录”该专…

OpenCV入门(五)快速学会OpenCV4文字绘制边界填充

OpenCV入门(五)快速学会OpenCV4文字绘制&边界填充 1.文字绘制 OpenCV中除了提供绘制各种图形的函数外,还提供了一个特殊的绘制函数,即在图像上绘制文字。 这个函数是putText(),它是命名空间cv2中的函数&#xff…

(二十三)操作系统-多生产者·多消费者问题

文章目录一、问题描述二、问题分析1. 关系分析2. 整理思路三、实现1. 代码2.如果不要互斥信号量3. 将盘子(缓冲区)容量设为2四、总结一、问题描述 桌子上有一只盘子,每次只能向其中放入一个水果。爸爸专向盘子中放苹果,妈妈专向盘…

用强化学习神包trl轻松实现GPT2可控文本生成

来源:投稿 作者:Sally can wait 编辑:学姐 模型github: lvwerra/trl: Train transformer language models with reinforcement learning. (github.com)https://github.com/lvwerra/trl 这个项目是复现 ”Fine-Tuning Language Models from H…

C++vector 简单实现

一。概述 vector是我们经常用的一个容器,其本质是一个线性数组。通过对动态内存的管理,增删改查数据,达到方便使用的目的。 作为一个线性表,控制元素个数,容量,开始位置的指针分别是: start …

Hive---拉链表

拉链表 文章目录拉链表定义用途案例全量流程增量流程合并过程第一步第二步第三步案例二(含分区)创建外部表orders增量分区表历史记录表定义 拉链表是一种数据模型,主要是针对数据仓库设计中表存储数据的方式而定义的,顾名思义&am…

从零开始学GeoServer源码十一(如何处理多个文件解析器Multipart Resolver引起的冲突问题)

目录前言1.现象2.排查问题3.找到问题4.解决问题5.总结前言 本文起源于我们遇到的一个问题,本来 GeoServer 使用的好好的,但是有天突然发现,无法在 GeoServer 中上传样式的 sld 文件了,报错 “No Multipart-config for Servlet” …

java.lang.IllegalArgumentException: itemView may not be null

报错截图:场景介绍:在使用recycleView 自动递增数据,且自动滚动到最新行; 当数据达到273条 时出现ANR;项目中 全部的列表适配器使用的三方库:BaseRecyclerViewAdapterHelper (很早之前的项目&am…