Java多线程 - - -线程安全问题

2023-11-01

Java- - - 多线程学习笔记(二)

线程安全问题的出现

当一个进程中的多个线程共享资源或数据的时候,就会出现安全隐患
例如,三个售票窗口同时售票,如果没有进行线程安全的处理,则会出现重票,错票等线程安全问题

package com.fff;

//实现Runnable接口
class TicketWindow implements Runnable{
    private static int ticketCount = 100;//三个线程的共享资源,用static

    //实现接口中的抽象方法
    @Override
    public void run() {
        while(true){
            if(ticketCount > 0){
                try {
                //使用sleep方法,使当前进程阻塞100ms,方便通过输出结果查看线程的状态
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+":卖票---票号为:"+ticketCount);
                ticketCount--;
            }else {
                break;
            }
        }
    }
}
public class Window {
    public static void main(String[] args){
        //创建实现类的对象
        TicketWindow ticketWindow = new TicketWindow();

        //将此对象作为参数传递到Thread类的构造器中
        Thread t1 = new Thread(ticketWindow);
        Thread t2 = new Thread(ticketWindow);
        Thread t3 = new Thread(ticketWindow);

        //设置三个线程的名字
        t1.setName("窗口一");
        t2.setName("窗口二");
        t3.setName("窗口三");

        //通过Thread类的对象调用start()方法,启动三个线程
        t1.start();
        t2.start();
        t3.start();
    }
}

在这里插入图片描述
通过输出结果可发现,三个窗口也就是三个进程在切换执行的过程中,出现错票和重票的情况,这就说明当前多线程任务是不安全的。

解决线程安全问题

在Java中,通过同步机制解决线程安全问题。

方式一:同步代码块

syncchronized(同步监视器){
//需要被同步的代码
}
说明**:操作共享数据的代码即为需要被同步的代码。**
共享数据:多个线程共同操作的变量;
同步监视器:俗称“”,任何一个类的对象都可以充当锁。
要求:多个线程必须共用同一把锁;

package com.fff;

//实现Runnable接口
class TicketWindow implements Runnable{
    private static int ticketCount = 100;//三个线程的共享资源,用static
    Object obj  = new Object();//任何一个类的对象都可以充当锁;
    //实现接口中的抽象方法
    @Override
    public void run() {
        while(true){
            synchronized (obj){
                //需要被同步的代码
                if(ticketCount > 0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+":卖票---票号为:"+ticketCount);
                    ticketCount--;
                }else {
                    break;
                }
            }

        }
    }
}
public class Window {
    public static void main(String[] args){
        //创建实现类的对象
        TicketWindow ticketWindow = new TicketWindow();

        //将此对象作为参数传递到Thread类的构造器中
        //一下三个线程共用一个实现类的对象,因此三个线程共用一把锁
        Thread t1 = new Thread(ticketWindow);
        Thread t2 = new Thread(ticketWindow);
        Thread t3 = new Thread(ticketWindow);

        //设置三个线程的名字
        t1.setName("窗口一");
        t2.setName("窗口二");
        t3.setName("窗口三");

        //通过Thread类的对象调用start()方法,启动三个线程
        t1.start();
        t2.start();
        t3.start();
    }
}

在这里插入图片描述
通过结果发现,没有再出现重票错票的情况,当一个线程没有执行完,哪怕是发生了阻塞,别的线程也不会执行,而是要等到当前线程执行完毕,其他的线程才会执行。
**注意:**在通过同步代码块的方式解决继承Thread类的线程安全问题时,一定要注意锁必须是唯一的。

方式二:同步方法

如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明为同步的;

package com.fff;

//实现Runnable接口
class TicketWindow implements Runnable{
    private static int ticketCount = 100;//三个线程的共享资源,用static
    Object obj  = new Object();//任何一个类的对象都可以充当锁;
    //实现接口中的抽象方法
    @Override
    public void run() {
        while(true){
            this.show();
        }
    }
    private synchronized void show(){//同步监视器:this
        if(ticketCount > 0){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+":卖票---票号为:"+ticketCount);
            ticketCount--;
        }
    }
}
public class Window {
    public static void main(String[] args){
        //创建实现类的对象
        TicketWindow ticketWindow = new TicketWindow();

        //将此对象作为参数传递到Thread类的构造器中

        Thread t1 = new Thread(ticketWindow);
        Thread t2 = new Thread(ticketWindow);
        Thread t3 = new Thread(ticketWindow);

        //设置三个线程的名字
        t1.setName("窗口一");
        t2.setName("窗口二");
        t3.setName("窗口三");

        //通过Thread类的对象调用start()方法,启动三个线程
        t1.start();
        t2.start();
        t3.start();
    }
}

同步方法仍然涉及到同步监视器,只不过不需要显示的声明。
非静态的同步方法,同步监视器是:this
静态的同步方法,同步监视器是:当前类本身
**注意:**当我们用同步方法方式解决继承Thread线程问题时,要注意,同步方法要是静态的,保证用的是同一个锁,所有的线程共用一个同步方法,同一个锁。

方式三:Lock锁

java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。
ReentrantLock类实现了Lock,可以显示加锁,释放锁。

package com.fff;

import java.util.concurrent.locks.ReentrantLock;

//实现Runnable接口
class TicketWindow implements Runnable{
    private static int ticketCount = 100;//三个线程的共享资源,用static

    //1.实例化ReentrantLock
    private ReentrantLock lock = new ReentrantLock();
    //实现接口中的抽象方法
    @Override
    public void run() {
        while(true){
            try{

                //2.调用锁定方法lock()
                lock.lock();
                if(ticketCount > 0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+":卖票---票号为:"+ticketCount);
                    ticketCount--;
                }else {
                    break;
                }
            }finally {
                //3.调用解锁方法unlock()
                lock.unlock();
            }



        }
    }
}
public class Window {
    public static void main(String[] args){
        //创建实现类的对象
        TicketWindow ticketWindow = new TicketWindow();

        //将此对象作为参数传递到Thread类的构造器中

        Thread t1 = new Thread(ticketWindow);
        Thread t2 = new Thread(ticketWindow);
        Thread t3 = new Thread(ticketWindow);

        //设置三个线程的名字
        t1.setName("窗口一");
        t2.setName("窗口二");
        t3.setName("窗口三");

        //通过Thread类的对象调用start()方法,启动三个线程
        t1.start();
        t2.start();
        t3.start();
    }
}

synchronized和Lock的区别

synchronized机制在执行完相应的同步代码之后,自动的释放同步监视器;
Lock需要手动的启动同步,同时结束同步也需要手动的实现

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java多线程 - - -线程安全问题 的相关文章

  • 在 Java 中连接和使用 Cassandra

    我已经阅读了一些关于 Cassandra 是什么以及它可以做什么的教程 但我的问题是如何在 Java 中与 Cassandra 交互 教程会很好 如果可能的话 有人可以告诉我是否应该使用 Thrift 还是 Hector 哪一个更好以及为什
  • 如何使用 Java 和 Selenium WebDriver 在 C 目录中创建文件夹并需要将屏幕截图保存在该目录中?

    目前正在与硒网络驱动程序和代码Java 我有一种情况 我需要在 C 目录中创建一个文件夹 并在该文件夹中创建我通过 selenium Web 驱动程序代码拍摄的屏幕截图 它需要存储在带有时间戳的文件夹中 如果我每天按计划运行脚本 所有屏幕截
  • Spring Batch 多线程 - 如何使每个线程读取唯一的记录?

    这个问题在很多论坛上都被问过很多次了 但我没有看到适合我的答案 我正在尝试在我的 Spring Batch 实现中实现多线程步骤 有一个包含 100k 条记录的临时表 想要在 10 个线程中处理它 每个线程的提交间隔为 300 因此在任何时
  • 在 HTTPResponse Android 中跟踪重定向

    我需要遵循 HTTPost 给我的重定向 当我发出 HTTP post 并尝试读取响应时 我得到重定向页面 html 我怎样才能解决这个问题 代码 public void parseDoc final HttpParams params n
  • JavaMail 只获取新邮件

    我想知道是否有一种方法可以在javamail中只获取新消息 例如 在初始加载时 获取收件箱中的所有消息并存储它们 然后 每当应用程序再次加载时 仅获取新消息 而不是再次重新加载它们 javamail 可以做到这一点吗 它是如何工作的 一些背
  • Spring Data JPA 应用排序、分页以及 where 子句

    我目前正在使用 Spring JPA 并利用此处所述的排序和分页 如何通过Spring data JPA通过排序和可分页查询数据 https stackoverflow com questions 10527124 how to query
  • 路径中 File.separator 和斜杠之间的区别

    使用有什么区别File separator和一个正常的 在 Java 路径字符串中 与双反斜杠相反 平台独立性似乎不是原因 因为两个版本都可以在 Windows 和 Unix 下运行 public class SlashTest Test
  • 禁止的软件包名称:java

    我尝试从数据库名称为 jaane 用户名 Hello 和密码 hello 获取数据 错误 java lang SecurityException Prohibited package name java at java lang Class
  • Java按日期升序对列表对象进行排序[重复]

    这个问题在这里已经有答案了 我想按一个参数对对象列表进行排序 其日期格式为 YYYY MM DD HH mm 按升序排列 我找不到正确的解决方案 在 python 中使用 lambda 很容易对其进行排序 但在 Java 中我遇到了问题 f
  • Java TestNG 与跨多个测试的数据驱动测试

    我正在电子商务平台中测试一系列商店 每个商店都有一系列属性 我正在考虑对其进行自动化测试 是否有可能有一个数据提供者在整个测试套件中提供数据 而不仅仅是 TestNG 中的测试 我尝试不使用 testNG xml 文件作为机制 因为这些属性
  • 为什么HashMap不能保证map的顺序随着时间的推移保持不变

    我在这里阅读有关 Hashmap 和 Hashtable 之间的区别 http javarevisited blogspot sg 2010 10 difference Between hashmap and html http javar
  • 使用Caliper时如何指定命令行?

    我发现 Google 的微型基准测试项目 Caliper 非常有趣 但文档仍然 除了一些示例 完全不存在 我有两种不同的情况 需要影响 JVM Caliper 启动的命令行 我需要设置一些固定 最好在几个固定值之间交替 D 参数 我需要指定
  • Java Integer CompareTo() - 为什么使用比较与减法?

    我发现java lang Integer实施compareTo方法如下 public int compareTo Integer anotherInteger int thisVal this value int anotherVal an
  • AWS 无法从 START_OBJECT 中反序列化 java.lang.String 实例

    我创建了一个 Lambda 函数 我想在 API 网关的帮助下通过 URL 访问它 我已经把一切都设置好了 我还创建了一个application jsonAPI Gateway 中的正文映射模板如下所示 input input params
  • Android 中麦克风的后台访问

    是否可以通过 Android 手机上的后台应用程序 服务 持续监控麦克风 我想做的一些想法 不断聆听背景中的声音信号 收到 有趣的 音频信号后 执行一些网络操作 如果前台应用程序需要的话 后台应用程序必须能够智能地放弃对麦克风的访问 除非可
  • 如何从指定日期获取上周五的日期? [复制]

    这个问题在这里已经有答案了 如何找出上一个 上一个 星期五 或指定日期的任何其他日期的日期 public getDateOnDay Date date String dayName 我不会给出答案 先自己尝试一下 但是 也许这些提示可以帮助
  • 如何在桌面浏览器上使用 webdriver 移动网络

    我正在使用 selenium webdriver 进行 AUT 被测应用程序 的功能测试自动化 AUT 是响应式网络 我几乎完成了桌面浏览器的不同测试用例 现在 相同的测试用例也适用于移动浏览器 因为可以从移动浏览器访问 AUT 由于它是响
  • 编译器抱怨“缺少返回语句”,即使不可能达到缺少返回语句的条件

    在下面的方法中 编译器抱怨缺少退货声明即使该方法只有一条路径 并且它包含一个return陈述 抑制错误需要另一个return陈述 public int foo if true return 5 鉴于Java编译器可以识别无限循环 https
  • 在 Maven 依赖项中指定 jar 和 test-jar 类型

    我有一个名为 commons 的项目 其中包含运行时和测试的常见内容 在主项目中 我添加了公共资源的依赖项
  • 当我从 Netbeans 创建 Derby 数据库时,它存储在哪里?

    当我从 netbeans 创建 Derby 数据库时 它存储在哪里 如何将它与项目的其余部分合并到一个文件夹中 右键单击Databases gt JavaDB in the Service查看并选择Properties This will

随机推荐

  • 不同文件夹(包)下的类调用

    1 直接调用 导入需求包名 使用方式 类名 方法名 参数列表 package cn edu360 import temporary Max public class packTest public static void main Stri
  • 每天一个小题目——喷水装置

    题目描述 小赛家有一块草坪 长为20米 宽为2米 妈妈要他给草坪浇水 在草坪上放置半径为Ri的喷水装置 每个喷水装置可以给以它为中心的半径为实数Ri 1 Ri 15 的圆形区域浇水 他家有充足的喷水装置i 1 i 600 个 并且一定能把草
  • 多态+多态对象模型

    多态 字面意思即为多种形态 C 多态性是通过虚函数来实现的 虚函数允许子类重新定义成员函数 而子类重新定义父类的做法称为覆盖或者称为重写 当使用基类的指针或引用调用重写的虚函数时 当指向父类调用的就是父类的虚函数 指向子类的就是子类的虚函数
  • 专题详解-5G接入控制:CAG新特性(3)-end

    相关文章会在公众号同步更新 公众号 5G通信大家学 持续更新的相关5G内容都是直接根据3GPP整理 保证更新内容的准确性 避免通过二手 甚至多手的资料 以讹传讹误导网友 稀稀拉拉经过这么长时间的分析 5G网络的接入控制基本算是分析完了 目前
  • 为什么使用GB28181而不是直接rtsp拉流

    1 GB sip和rtsp 实际上 sip协议和rtsp协议大同小异 并没有什么本质上得不同 那为什么我们不使用rtsp 而转而使用GB28181国标得sip协议 原因是 使用得方向不同 我们使用rtsp拉流是作为客户端 这时摄像机是服务端
  • 【问题解决】partially initialized module ‘cv2‘ has no attribute

    使用 MMOCR 时报错 partially initialized module cv2 has no attribute 可能是 opencv 的版本问题 也可能是 opencv 和当前环境不能完全匹配的问题 可以使用下面的方法重新安装
  • 分享一个 ChatGPT可免费使用的AI助手

    现在 多种行业都已经开始广泛地采用聊天机器人ChatGPT 有一个与之相关的国内免费网站可以供大家使用 多御浏览器 这是一款安全快速 高效稳定的浏览器 该网站客户端软件中 有很多实用工具 其中之一就是当下非常流行的 ChatGPT 这对于开
  • python 更换pip安装源

    pip源 默认从国外源安装 安装速度比较慢 现在我们指定国内源安装源 阿里源 豆瓣源 Ubuntu源 1 指令配置源 pip3 install xlrd i https pypi tuna tsinghua edu cn simple 2
  • Qt - QSetting的使用

    欢迎转载 请注明出处 https blog csdn net qq 39453936 spm 1010 2135 3001 5343 原文链接 https blog csdn net qq 39453936 article details
  • Allegro快捷键

    env文件替换路径C Cadence SPB 16 6 share pcb text
  • mac启动pg数据库失败 “Is another postmaster (PID 370) running in data directory“

    解决办法 进入目录 usr local var postgres 删除文件 postmaster pid 启动pg命令 pg ctl D usr local var postgres l usr local var postgres ser
  • 挖矿病毒的特点

    挖矿病毒的特点 1 文件 定时任务删除失败 文件只读属性保护 2 文件 定时任务删完又出现 系统文件替换 下载进程残留 3 病毒进程刚刚删完又被拉起 恶意进程守护 4 主机严重卡顿但找不到挖矿进程 系统命令劫持 5 主机杀干净后一段时间又出
  • 【云原生之kubernetes】在kubernetes集群下的映射外部服务—Eendpoint

    云原生之kubernetes 在kubernetes集群下的映射外部服务 Eendpoint 一 Eendpoint介绍 1 endpoint简介 2 endpoint的使用 二 检查本地k8s状态 1 检查工作节点状态 2 检查系统pod
  • Modelsim、Matlab在远程桌面下打开的异常及处理

    此方法可以解决远程桌面下启动MATLAB时的License Manager Error 103错误 也能够解决远程桌面下启动modelsim的错误 方法如下 1 打开C Program Files MATLAB R2015b license
  • kafka基础介绍

    目录 前言 一 kafka架构 1 kafka基础架构 2 kafka多副本架构 二 kafka基础概念 1 produce 2 Consumer 3 Broker 4 Topic 5 Partition 6 Replicas 7 Offs
  • C++数据结构X篇_12_树的基本概念和存储

    学习二叉树之前先学习树的概念 文章目录 1 树的基本概念 1 1 树的定义 1 2 树的特点 1 3 若干术语 2 树的表示法 2 1 图形表示法 2 2 广义表表示法 3 树的存储 3 1 双亲表示法 保存父节点关系 3 2 孩子表示法
  • 伴随矩阵的特征值和特征向量

    结 论 伴 随 矩 阵 的 特
  • winsock

    今天上午学习了 winsock的使用的过程 现将学习的收获总结如下 Winsock的属性 1 Localport 设定本机的端口号 端口号从0 65536 其中1024一下一般为专用的端口 如21为ftp默认端口 80为http默认端口 2
  • 关于对CSDN这些技术博客类网站的一些个人看法

    今天这篇文章其实是自己的一个分享与感悟 分享一下自己从技术小白到有点小实力的这个过程中对自己帮助的一些技术博客类网站的看法 关于我为什么会想写这么一篇文章呢 起因是我有一次查CSDN网站时候 看到了一篇文章 第一眼看到这篇文章的时候我觉得首
  • Java多线程 - - -线程安全问题

    Java 多线程学习笔记 二 线程安全问题的出现 当一个进程中的多个线程共享资源或数据的时候 就会出现安全隐患 例如 三个售票窗口同时售票 如果没有进行线程安全的处理 则会出现重票 错票等线程安全问题 package com fff 实现R