多线程-*同步代码块*解决两种线程创建方式的线程安全问题

news2024/12/27 12:35:21

线程安全问题及解决

当我们使用多个线程访问同一资源(可以是同一个变量、同一个文件、同一条记录等)的时候,若多个线程只有读操作,那么不会发生线程安全问题。但是多个线程中对资源有读和写的操作。就容易出现线程安全问题。

举例:

在这里插入图片描述

同一个资源问题和线程安全问题

案例:

火车站要卖票,我们模拟火车站的卖票过程。因为疫情期间,本次列车的座位共100个(即,只能出售100张火车票)。我们来模拟车站的售票窗口,实现多个窗口同时售票的过程。注意:不能出现错票、重票。

例题: 开启三个窗口售票,总票数为100张。

分别使用两种线程创建方式实现:

理想状态:

在这里插入图片描述

极端状态:

在这里插入图片描述

实现Runnable接口方式:

package thread.demo03_threadsafe.notsafe;

//使用实现Runnable接口的方式,实现卖票

public class WindowTest {
    public static void main(String[] args) {

        SaleTicket s = new SaleTicket();

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

        t1.setName("窗口一");
        t2.setName("窗口二");
        t3.setName("窗口三");

        t1.start();
        t2.start();
        t3.start();
        //正常运行后发现每个窗口都卖了100号票
    }
}


class SaleTicket implements Runnable{
    int ticket = 100;

    @Override
    public void run() {

        while (true){
            if (ticket > 0){

                try {
                    Thread.sleep(10);//有了这一段运行后看结果竟然还出现-1号票,这就是线程的安全问题
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                System.out.println(Thread.currentThread().getName() + "售票,票号为: " + ticket);
                ticket--;
            }else {
                break;
            }
        }

    }
}

继承Thread方式

package thread.demo03_threadsafe.notsafe;

//使用继承Thread类的方式,实现卖票。

public class WindowTest1 {
    public static void main(String[] args) {

        SaleTicket1 s1 = new SaleTicket1("窗口1");
        SaleTicket1 s2 = new SaleTicket1("窗口2");
        SaleTicket1 s3 = new SaleTicket1("窗口3");

        s1.start();
        s2.start();
        s3.start();
        //正常运行后还是有重票三个100,甚至错票-1

    }
}


class SaleTicket1 extends Thread{

    public SaleTicket1() {
    }

    public SaleTicket1(String name) {
        super(name);
    }

    static int ticket = 100;//没有static直接300张票,每个窗口都卖1——100
    //一个对象一份的是实例变量。
    //所有对象一份的是静态变量。

    @Override
    public void run() {
        while (true){
            if (ticket > 0){
                System.out.println(Thread.currentThread().getName() + "票号为: " + ticket);
                ticket--;
            }else {
                break;
            }

        }
    }
}

以上两种线程创建方式运行后都出现了线程安全问题!!


怎么解决?看下面

线程的安全问题与线程的同步机制

1. 多线程卖票出现的问题:出现了重票和错票


2.什么原因导致的?  线程1操作ticket的过程中,尚未结束的情况下,其他线程也参与进来,对ticket进行操作。


3.如何解决? 必须保证一个线程a在操作ticket的过程中,其他线程必须等待,
           直到线程a操作ticket结束以后,其它线程才可以进来继续操作ticket。(ticket是共享数据)
           比如上厕所几个坑位,坑位就是共享数据,排队进去后,上完,后面的人才能进来,不能一次进去好几个人,这样才安全。


4.Java是如何解决线程的安全问题? 使用线程的同步机制。

方式1:同步代码块

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

说明:
> 需要被同步的代码,及为操作共享数据的代码
> 共享数据:即多个线程都需要操作的数据。比如:ticket
> 需要被同步的代码,在被synchronized包裹以后,就使得一个线程在操作这些代码的过程中,其他线程必须等待。
> 同步监视器,俗称锁。哪个线程获取了锁,哪个线程就能执行需要被同步的代码。
              比如一个坑位,有个门锁,你进去把门锁上别人就进不来了,上完厕所把锁打开其他人才能进来。
> 同步监视器,可以使用任何一个类的对象充当。但是,多个线程必须共用同一个同步监视器。

注意:在实现Runnable接口的方式中,同步监视器可以考虑使用:this。
      在继承Thread类的方式中,同步监视器要慎用this,可以考虑使用:当前类.class*下面的同步方法解决安全问题下节课看*
方式2:同步方法

说明:



5.synchronized的好处:

使用 同步代码块 解决两种线程创建方式的线程安全问题

解决实现Runnable接口方式出现的线程安全问题:

package thread.demo03_threadsafe.runnablesafe;

//使用实现Runnable接口的方式,实现卖票。---> 存在线程安全问题
//使用同步代码块解决上述卖票中的线程安全问题。

public class WindowTest {
    public static void main(String[] args) {

        SaleTicket s = new SaleTicket();

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

        t1.setName("窗口一");
        t2.setName("窗口二");
        t3.setName("窗口三");

        t1.start();
        t2.start();
        t3.start();
        //运行后正常,解决了线程安全问题,达到了预期结果。
    }
}


class SaleTicket implements Runnable{
    int ticket = 100;//ticket是共享数据
    Object obj = new Object();
    Dog dog = new Dog();

    @Override
    public void run() {

//        synchronized(this){ //全部用synchronized包起来运行查看结果全是窗口一,一个线程进来一直循环完才出来,不合适
        while (true){

            try {
                Thread.sleep(5);   //试了两次发现运行结果全是同一个窗口卖完了,这段代码就让进来的睡一下,其他的窗口就能获取到了。
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

//            synchronized(obj){    //要想保证安全性,对象必须是唯一的。 obj是唯一的?yes
//            synchronized(dog){    //dog:是唯一的?yes
            //this:最方便的。它是唯一的?yes,this是run方法的对象,也就是SaleTicket的对象,启动类中new了一下它,就是题目中的s,是唯一的。
            synchronized(this){ //这个可以
//            synchronized (SaleTicket.class){  后面学的这个对象也是唯一的,而且靠谱,也能用。

                if (ticket > 0){

                    try {
                        Thread.sleep(10);//有了这一段运行后看结果竟然出现-1号票,这就是线程的安全问题
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    System.out.println(Thread.currentThread().getName() + "售票,票号为: " + ticket);
                    ticket--;
                }else {
                    break;
                }
            }

        }

    }
}

class Dog{

}

分析线程同步机制原理:

在这里插入图片描述

解决继承Thread方式出现的线程安全问题:

package thread.demo03_threadsafe.threadsafe;

//使用继承Thread类的方式,实现卖票。
//使用同步代码块的方式解决线程安全问题。

public class WindowTest {
    public static void main(String[] args) {

        SaleTicket1 s1 = new SaleTicket1("窗口1");
        SaleTicket1 s2 = new SaleTicket1("窗口2");
        SaleTicket1 s3 = new SaleTicket1("窗口3");

        s1.start();
        s2.start();
        s3.start();
        //运行后正常,解决了线程安全问题,达到了预期结果。

    }
}


class SaleTicket1 extends Thread{

    public SaleTicket1() {
    }

    public SaleTicket1(String name) {
        super(name);
    }

    static int ticket = 100;//没有static直接300张票,每个窗口都卖1——100
    //一个对象一份的是实例变量。
    //所有对象一份的是静态变量。

    static Object obj = new Object();//加了static就是全局唯一的。

    @Override
    public void run() {
        while (true){

//            synchronized (this) {    //this不安全,this就是这个方法的对象,也就是SaleTicket1类的对象,被new了三次,不是唯一的,不安全。
//            synchronized (obj) {    //obj:使用static就能保证其唯一性,这个可以用,但是还有没有现成的而且还唯一的?
            synchronized (SaleTicket1.class) {    //这里应该放对象,看起来像类,这个比较超纲,后续学到反射讲解,这其实也是一个对象。
                                                  //结构:Class clz = SaleTicket1.class    SaleTicket1.class本来就是Class的对象,是唯一的。
                                                  //大写Class就相当于一个类,它的对象就是具体的某个具体的类。

                if (ticket > 0){

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

                    System.out.println(Thread.currentThread().getName() + "票号为: " + ticket);
                    ticket--;
                }else {
                    break;
                }
            }


        }
    }
}

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

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

相关文章

PyTorch 深度学习 || 专题三:PyTorch 数据的准备

PyTorch 数据的准备 1. 生成数据的准备工作 import torch import torch.utils.data as Data#准备建模数据 x torch.unsqueeze(torch.linspace(-1, 1, 500), dim1) # 生成列向量 y x.pow(3) # yx^3#设置超参数 batch_size 15 # 分块大小 torch.manual_seed(10) # 设置种子点…

国自然发文:这种行为将永久取消基金申请资格

函评阶段这些行为被基金委禁止 国家自然科学基金委重拳出击打击"打招呼"现象,出台《国家自然科学基金项目评审请托行为禁止清单》,禁止科研人员、依托单位、评审专家和基金委工作人员24种违规行为。 主要内容如下: 一、科研人员禁止清单(7项) 主要包括…

初阶数据结构——二叉树

目录 树的概念及结构树的概念树的相关概念树的表示树在实际中的运用 二叉树概念及结构特殊的二叉树二叉树的性质二叉树的存储结构 二叉树的顺序结构及实现二叉树的顺序结构堆的概念及结构堆的实现堆的结构:初始化堆:销毁堆:插入数据&#xff…

通过maven配置不同的开发环境

前言 项目有开发、测试、生产至少有这三个环境, 所需要的配置信息肯定不一样, 比如需要开发环境的时候,注解掉测试和生产的配置信息,打开开发的配置信息。 后来工作接触到新项目,发现是通过maven来控制加载不同的配置…

第55讲:Python函数形参、实参的语法格式汇总以及应用小案例

文章目录 1.Python函数中各种参数的汇总1.1.常规参数1.2.参数设置默认值1.3.强制传参时使用关键字传参1.4.个数可变的形参1.5.将序列中的每个元素都转换为位置实参1.6.将字典中的每个元素都转换为关键字实参 2.小案例练习Python中的各种函数参数2.1.案例一2.2.案例二 1.Python函…

React-reacte-app项目实现antD按需加载(2023)

出现的问题: 项目打包后,由于引入了antD,所以打包后的mani.js体积会非常大。相当于引入了全部的antD的代码。所以可以做一个优化:通过script标签引入antD,将其挂载在window上。使用antD组件的使用,直接从w…

2.矢量分析

目录 一.标量函数和矢量函数 二.矢端曲线 三.矢量函数导数和微分 1.导数 2.导数的几何意义 3.微分 4.矢量导数性质 5.例题 四.矢量导数的应用 1.几何应用 1.曲线的切线和法平面 2.曲面的法线和法平面 2.物理应用 3.两大典型问题 五.矢量函数的积分 如果第一章我…

WOT全球技术创新大会开幕倒计时——好内容才是永远的底气

距离6月16日-17日召开的WOT全球技术创新大会还有半个月时间,所有讲师、议题均已就位。本届WOT不仅囊括2023年最火的技术——AIGC、大模型、大算力,也有被技术人永恒关注的热点话题——多云实践、业务架构演进、效能提升。 筹备数月之久,50来…

Intel® 以太网800 系列网络适配器 – DPDK上的性能演进

1. 介绍 Intel以太网800系列网络适配器提供卓越的性能,以满足各种工作负载的要求。800系列提供了数据包分类和排序优化、硬件增强的定时能力以及完全可编程的流水线。Intel的以太网产品组合始终提供可靠的体验和经过验证的互操作性。 在DPDK中,ICE轮询模…

CDN控制平台操作指引之账号注册及添加域名

一、注册账户及登录 (一)请确认您已经注册了火伞云账号并完成账号实名认证。如果未注册,请先完成账号注册和实名认证。 (二)登录火伞云融合CDN管理平台。 二、添加加速域名 使用火伞云融合CDN加速指定网站&#xff…

NodeJs之Ajax技术

1. 初识Ajax ./public/Ajax.html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content&qu…

ArcGIS Pro地图和场景

目录 1 地图和场景 1.1 地图 1.2 场景 2 图层 2.1 图层类型 2.1.1 要素图层&#xff08;Feature layers&#xff09; 2.1.2 栅格图层&#xff08;Raster layers&#xff09; 2.1.3 场景图层&#xff08;Scene layers&#xff09; 2.1.4 服务图层&#xff08;Service la…

机器视觉怎么对陶瓷板的外观尺寸进行自动检测?

随着工业自动化的不断发展&#xff0c;机器视觉技术在制造业中的应用越来越广泛。在陶瓷板行业中&#xff0c;机器视觉技术可以用于自动检测陶瓷板的外观尺寸&#xff0c;提高生产效率和产品质量。下面我们来介绍机器视觉如何对陶瓷板的外观尺寸进行自动检测。 一、检测原理 …

第2关:socket编程

第2关&#xff1a;socket编程 任务描述相关知识1、Socket2、socket函数3、bind函数4、listen、connetct函数5、accept函数6、read、write函数7、close、shutdown函数8、三次握手9、数据传输10、四次挥手 编程要求及注意事项测试说明参考代码 任务描述 本关任务&#xff1a; 编…

轻松掌握,板对板连接器选型指南

一、什么是板对板连接器&#xff1f; 板对板连接器(简称BTB)&#xff0c;是精密电路中电源和信号耦合连接必不可少的器件。随着产品复杂度和电路设计规模的不断提升&#xff0c;板对板连接器也沿着市场需求向着高速率&#xff0c;高功率密度&#xff0c;轻薄化几个方向发展。 …

chatgpt赋能python:为什么Python不适合做SEO?——解析

为什么Python不适合做SEO&#xff1f;——解析 Python作为一种非常方便的编程语言&#xff0c;在数据处理、人工智能等方面有着广泛的应用。然而&#xff0c;在SEO领域&#xff0c;Python似乎并不受欢迎。本文将从Python不适用于SEO的几个方面做出详细的介绍&#xff0c;并为读…

WPF .Net6中使用Prism

.NET 6 是微软长期支持&#xff0c;并在移动、桌面、IoT 和云应用之间统一了 SDK、基础库和运行时。 Prism为程序设计提供指导,旨在帮助用户更加容易的设计和构建丰富、灵活、易于维护WPF桌面应用程序。Prism使用设计模式&#xff08;如MVVM,复合视图,事件聚合器&#xff09;…

02 【el和data的两种写法 MVVM模型】

02 【el和data的两种写法 MVVM模型】 1.el和data的两种写法 el有2种写法 创建Vue实例对象的时候配置el属性 先创建Vue实例&#xff0c;随后再通过vm.$mount(‘#root’)指定el的值 data有2种写法 对象式&#xff1a;data&#xff1a; { }函数式&#xff1a;data() { retur…

国产给力啊啊啊----国产MCU-CW32F030开发学习

国产MCU-CW32F030开发学习 1. 相关资料下载 1.1 武汉芯源半导体 武汉芯源半导体官网 武汉芯源半导体 武汉芯源 21ic 电子论坛 21ic 电子论坛 1.2 CW32F030系列资料 CW32F030技术文档 • 内核&#xff1a;ARM Cortex-M0 – 最高主频64MHz • 工作温度&#xff1a;-40℃ 至…

Java Number Math character类

文章目录 一、Java Number& Math 类1、Java Math 类2、Number & Math 类方法3、Math 的 floor,round 和 ceil 方法实例比较 二、Java Character 类1、转义序列2、character 方法 总结 一、Java Number& Math 类 一般地&#xff0c;当需要使用数字的时候&#xff0…