多线程2(同步代码块+同步方法+同步锁+死锁)

2023-11-19

一(多线程同步)

多线程的并发执行可以提高程序的效率,但是当多个线程去访问同一个资源时,有时也会引发一些安全性问题。例如:统计一个班上的学生人数时,学生有进有出会影响最终学生人数。为了解决这样的问题,需要实现多线程的同步,即限制某个资源在同一时刻只能被一个线程访问。

package 多线程;
class TicketThread implements Runnable{
    int num=10;
    @Override
    public void run() {
        while (true){
            if(num>0){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"正在售卖第:"+num--+"号票");
            }
        }
    }
}
public class SaleThread{
    public static void main(String[] args) {
        TicketThread t=new TicketThread();
        Thread t1=new Thread(t,"窗口1");
        Thread t2=new Thread(t,"窗口2");
        Thread t3=new Thread(t,"窗口3");
        Thread t4=new Thread(t,"窗口4");
        t1.start();
        t2.start();
        t3.start();
        t4.start();

    }
}

从最后几行输出结果可看出,这种现象是不应该存在的,说明多线程在售票时出现了安全问题。在售票程序的while循环中添加了sleep()方法,这样就模拟了售票过程的延迟。由于线程有延迟,当票号减为1时,假设窗口2线程出售1号票,对票号进行判断,进入while循环,在售票之前通过sleep()方法模拟售票时耗时操作,这时窗口1线程会进行售票,由于此时票号仍为1,因此窗口1线程也会进入循环,同理,4个线程都会进入循环,休眠结束后,4个线程都会进行售票,这样就将票号减了4次,结果出现了0,-1,-2等票号。

二(同步代码块)

线程安全问题是由多个线程同时处理共享资源所导致的,要想解决线程安全问题,就必须保证处理共享资源的代码在任意时刻只能有一个线程在执行。为此,Java提供了线程同步机制。当多个线程使用同一个共享资源时,可以将处理共享资源的代码放在一个使用synchronized关键字来修饰的代码块中,这段代码被称作“同步代码块”,语法格式如下:

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

       1.需要被同步的代码指的是,操作共享数据的代码
       2.共享数据是:多个线程共同操作的变量
       3. 同步监视器:俗称 锁.任何一个类的对象,都可以来当锁
   要求:多个线程必须要公用一把锁 

 同步代码块中的锁对象可以是任意类型的对象,但多个线程共享的锁对象必须是相同的。“任意”说的是共享锁对象的类型。所以,锁对象的创建代码不能放到run()方法中,否则每个线程运行到run()方法都会创建一个新对象,这样每个线程都会有一个不同的锁,线程之间便不能产生同步的效果。

package 多线程;
class TicketThread1 implements Runnable{
    int num=10;
    @Override
    public void run() {
        while (true){
            synchronized (this){
                if(num>0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"正在售卖第:"+num--+"号票");
                }
            }

        }
    }
}
public class SaleThread1{
    public static void main(String[] args) {
        TicketThread1 t=new TicketThread1();
        Thread t1=new Thread(t,"窗口1");
        Thread t2=new Thread(t,"窗口2");
        Thread t3=new Thread(t,"窗口3");
        Thread t4=new Thread(t,"窗口4");
        t1.start();
        t2.start();
        t3.start();
        t4.start();

    }
}

从运行结果可看出,使用同步代码块后,不再出现票号为负数的情况,说明解决了线程安全性问题。 

三(同步方法)

同样,在方法前面使用synchronized关键字来修饰,被修饰的方法称为“同步方法”,它能实现和同步代码块一样的功能,具体格式如下:

[修饰符] synchronized 返回值类型 方法名(方法参数){ 方法体}

被synchronized修饰的方法在某一时刻只允许一个线程访问,访问该方法的其他线程都会阻塞,指导当前的线程访问完毕后,其他线程才有机会执行。 

package 多线程;

public class SaleThread2 {
    public static void main(String[] args) {
        TicketThread2 t=new TicketThread2();
        Thread t1=new Thread(t,"窗口1");
        Thread t2=new Thread(t,"窗口2");
        Thread t3=new Thread(t,"窗口3");
        Thread t4=new Thread(t,"窗口4");
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }

}
class TicketThread2 implements Runnable{
    int num=10;
    @Override
    public void run() {
        while (true){
            sale();
        }
    }
    public synchronized void sale( ){
        if(num>0){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"正在售卖第:"+num--+"号票");
        }
    }
}

四(同步锁)

从JDK5开始,新增Lock锁方式解决线程安全问题。 Lock接口是控制多个线程对共享资源进行访问的工具,锁提供了对共享资源的独占访问,每次只有一个线程对Lock对象加锁。通过Lock接口的实现类ReentrantLock来创建一个Lock锁对象,并通过Lock锁对象的lock()方法和unlock()对核心代码块进行上锁和释放锁。为了保证所有情况下都能正常解锁以确保其他线程可以执行,通常强狂下会在finally{ }代码块中调用unlock()方法来解锁。

    lock():调用锁定方法,锁定当前线程
    unlock():释放此锁,必须释放锁的

package 多线程;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockThread {
    public static void main(String[] args) {
        TicketThread3 t=new TicketThread3();
        Thread t1=new Thread(t,"窗口1");
        Thread t2=new Thread(t,"窗口2");
        Thread t3=new Thread(t,"窗口3");
        Thread t4=new Thread(t,"窗口4");
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}

class TicketThread3 implements Runnable{
int num=10;
Lock lock=new ReentrantLock();//定义一个Lock锁对象
    @Override
    public void run() {
        while(true){
            lock.lock();//对代码块进行加锁
            if (num>0){
                try {
                    Thread.sleep(100);
                    System.out.println(Thread.currentThread().getName()+"正在售卖第:"+num--+"号票");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    lock.unlock();//执行完代码后释放锁
                }
            }
            else{
                lock.unlock();//用完锁后当前线程,在此不考虑分布式的情况,及时的把锁给释放掉就行。
                break;
            }
        }
    }
}

五(死锁)

死锁:  不同线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁。

注意:出现死锁,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续操作,所以我们在使用同步的时候要避免死锁现象产生。

如何避免死锁产生:
        1.尽量避免同步的嵌套
        2.尽量避免同步资源的定义
        3.专门的算法/原则

package 多线程;

public class ThreadDemo3 {
    public static void main(String[] args) {
        StringBuffer sb1=new StringBuffer();
        StringBuffer sb2=new StringBuffer();
        //分别做两个线程对sb1 和sb2追加
        new Thread(){
            @Override
            public void run() {
                synchronized (sb1){
                    sb1.append("a");
                    sb2.append("1");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (sb2){
                        sb1.append("b");
                        sb2.append("2");
                        System.out.println(this.getName()+sb1);
                        System.out.println(this.getName()+sb2);
                    }
                }
            }
        }.start();
        new Thread(){
            @Override
            public void run() {
                synchronized (sb2){
                    sb1.append("c");
                    sb2.append("3");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (sb1){
                        sb1.append("d");
                        sb2.append("4");
                        System.out.println(this.getName()+sb1);
                        System.out.println(this.getName()+sb2);
                    }
                }
            }
        }.start();;
    }
}

案例:使用A、B、C3个线程输出,要求:A输出1-5,B输出6-10,C输出11-15。

package 多线程;

public class ThreadDemo4 {
    public static void main(String[] args) {
        ShowNum sn=new ShowNum();
        Thread a=new Thread(sn,"A线程");
        Thread b=new Thread(sn,"B线程");
        Thread c=new Thread(sn,"C线程");
        a.start();
        b.start();
        c.start();
    }
}
class ShowNum implements Runnable{
int num=0;
    @Override
    public void run() {
        while (true){
            synchronized (this){
                    if(num< 15){
                        System.out.println(Thread.currentThread().getName()+"-----"+(++num));
                        notifyAll();
                        if(num%5==0){
                            //让出cpu还要释放锁
                            try {
                                wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }else {
                        break;
                    }
                }
            }
        }
    }

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

多线程2(同步代码块+同步方法+同步锁+死锁) 的相关文章

  • 在 mvn 命令中指定 pom.xml 并混合其他项目的目标

    我有多个问题 我可以在 mvn 命令中指定 pom xml 吗 在当前项目上执行 mvn 命令时 我可以混合另一个项目的目标吗 例如 mvn clean otherproject comple otherproject install ot
  • JDK 文档是语言规范的一部分吗?

    只有一名官员Java语言规范 https docs oracle com javase specs jls se8 html index html所有 Java 实现都必须遵守它 API文档怎么样 所有Java实现都需要遵守吗这个版本 ht
  • 通过SOCKS代理连接Kafka

    我有一个在 AWS 上运行的 Kafka 集群 我想用标准连接到集群卡夫卡控制台消费者从我的应用程序服务器 应用程序服务器可以通过 SOCKS 代理访问互联网 无需身份验证 如何告诉 Kafka 客户端通过代理进行连接 我尝试了很多事情 包
  • 如何在 Firebase 远程配置中从 JSON 获取值

    我是 Android 应用开发和 Firebase 的新手 我想知道如何获取存储在 Firebase 远程配置中的 JSONArray 文件中的值 String 和 Int 我使用 Firebase Remote Config 的最终目标是
  • JVisualVM/JConsole 中的 System.gc() 与 GC 按钮

    我目前正在测试处理 XML 模式的概念验证原型 并围绕一个非常消耗内存的树自动机外部库 我已经获得了源代码 构建 我想绘制 真实峰值 堆 随着模式大小的增加 不同运行的内存消耗 使用的指标符合我的目的并且不会影响问题 或者至少是它的合理近似
  • Spring Boot自动装配存储库始终为空[重复]

    这个问题在这里已经有答案了 每次我进入我的服务类时 存储库似乎都没有自动连接 因为它不断抛出 NullPointerException 谁能帮我检查一下我缺少什么吗 这是我的代码 演示应用程序 java package com exampl
  • 在 Wildfly 中与 war 部署共享 util jar 文件

    假设我有一个名为 util jar 的 jar 文件 该 jar 文件主要包含 JPA 实体和一些 util 类 无 EJB 如何使这个 jar 可用于 Wildfly 中部署的所有 war 无需将 jar 放置在 war 的 WEB IN
  • ConcurrentHashMap 内部是如何工作的?

    我正在阅读有关 Java 并发性的 Oracle 官方文档 我想知道Collection由返回 public static
  • 是否可以通过编程方式查找 logback 日志文件?

    自动附加日志文件以支持电子邮件会很有用 我可以以编程方式设置路径 如以编程方式设置 Logback Appender 路径 https stackoverflow com questions 3803184 setting logback
  • 如何检测 Java 字符串中的 unicode 字符?

    假设我有一个包含 的字符串 我如何找到所有这些 un icode 字符 我应该测试他们的代码吗 我该怎么做呢 例如 给定字符串 A X 我想将其转换为 AYXY 我想对其他 unicode 字符做同样的事情 并且我不想将它们存储在某种翻译映
  • 如何在 Spring 属性中进行算术运算?

  • 使用 Mockito 模拟某些方法,但不模拟其他方法

    有没有办法使用 Mockito 模拟类中的某些方法 而不模拟其他方法 例如 在这个 诚然是人为的 Stock我想嘲笑的班级getPrice and getQuantity 返回值 如下面的测试片段所示 但我想要getValue 执行乘法 如
  • Freemarker 和 Struts 2,有时它计算为序列+扩展哈希

    首先我要说的是 使用 Struts2 Freemarker 真是太棒了 然而有些事情让我发疯 因为我不明白为什么会发生这种情况 我在这里问是因为也许其他人有一个想法可以分享 我有一个动作 有一个属性 说 private String myT
  • 返回 Java 8 中的通用函数接口

    我想写一种函数工厂 它应该是一个函数 以不同的策略作为参数调用一次 它应该返回一个函数 该函数根据参数选择其中一种策略 该参数将由谓词实现 嗯 最好看看condition3为了更好的理解 问题是 它没有编译 我认为因为编译器无法弄清楚函数式
  • 使用布尔值进行冒泡排序以确定数组是否已排序

    我有以下用于冒泡排序的代码 但它根本不排序 如果我删除布尔值那么它工作正常 我知道 由于我的 a 0 小于所有其他元素 因此没有执行交换 任何人都可以帮助我解决这个问题 package com sample public class Bub
  • JMenu 中的文本居中

    好吧 我一直在网上寻找有关此问题的帮助 但我尝试的任何方法似乎都不起作用 我想让所有菜单文本都集中在菜单按钮上 当我使用setHorizontalTextPosition JMenu CENTER 没有变化 事实上 无论我使用什么常量 菜单
  • 是否可以使用 Java Guava 将函数应用于集合?

    我想使用 Guava 将函数应用于集合 地图等 基本上 我需要调整 a 的行和列的大小Table分别使所有行和列的大小相同 执行如下操作 Table
  • Java Swing:需要一个高质量的带有复选框的开发 JTree

    我一直在寻找一个 Tree 实现 其中包含复选框 其中 当您选择一个节点时 树中的所有后继节点都会被自动选择 当您取消选择一个节点时 树中其所有后继节点都会自动取消选择 当已经选择了父节点 并且从其后继之一中删除了选择时 节点颜色将发生变化
  • Resteasy 可以查看 JAX-RS 方法的参数类型吗?

    我们使用 Resteasy 3 0 9 作为 JAX RS Web 服务 最近切换到 3 0 19 我们开始看到很多RESTEASY002142 Multiple resource methods match request警告 例如 我们
  • 如何重新启动死线程? [复制]

    这个问题在这里已经有答案了 有哪些不同的可能性可以带来死线程回到可运行状态 如果您查看线程生命周期图像 就会发现一旦线程终止 您就无法返回到新位置 So 没有办法将死线程恢复到可运行状态 相反 您应该创建一个新的 Thread 实例

随机推荐

  • 智能检测与控制

    智能检测与控制在智能工厂各层次中的应用 智能工厂各层次定义的功能以及各种系统 设备在不同层次上的分配如下 1 计划层 实现面向企业的经营管理 如接收订单 建立基本生产计划 如原料使用 交货 运输 确定库存等级 保证原料及时到达正确的生产地点
  • MATLAB金融工具箱(二)--执行常见的金融任务

    二 执行常见的金融任务 1 简介 金融工具箱包含了可以执行许多常见的金融任务的函数 包括 l 处理和转换日期 2 4页 日历功能可以将日期在不同格式之间进行转换 包括Excel格式 并决定未来和过去的日期 分辨假期和工作日 计算日期之间的时
  • 【Qt开发】编译时报"undefined reference to"问题的解决方案

    1 出现原因分析 Undefined reference to 错误 这类错误是在连接过程中出现的 可能有两种原因 1 是使用者自己定义的函数或者全局变量所在源代码文件 没有被编译 连接 2 干脆还没有定义 这需要使用者根据实际情况修改源程
  • 基于矩阵求解多元线性回归

    多元线性回归法也是深度学习的内容之一 用java实现一下多元线性回归 一元线性回归的公式为 y a x b 多元线性回归的公式与一元线性回归的公式类似 不过是矩阵的形式 可以表示为Y AX b 其中 Y是样本输出的合集 X是样本输入的合集
  • 协议转换网关 通用服务器,数据采集、协议转换网关

    产品名称 数据采集 协议转换网关 产品链接 http cn trustexporter com cp luomicekong o4256770 htm 手机版链接 https m trustexporter com cz4256770 ht
  • 【Linux】安装Ubuntu18.04时常见的一些问题

    1 vim sudo apt get install vim 出现 Command vim not found 解决办法 输入sudo apt install vim即可 sudo apt install vim 如果安装失败最后一行显示
  • TCP对方关闭对应的进程,调用Send将会产生Broken pipe信号默认会关闭进程

    解决 Linux下send函数 Broken pipe错误的解决方法 http www xootus net cache shtml 52 content 491 htm
  • 郝斌老师C语言零基础自学专讲180集完整版

    从C语言基础到后面的指针之类的都有 讲得还不错 下载地址 http pan baidu com share link shareid 43714 uk 1711320320
  • 编写一套工具库并上传NPM

    你的 工具箱 开箱即可用的 directive utils 说明 vue3 directive tools 是一个方便在 Vue 3 Ts 项目中快速使用的 directive tool 的 npm 插件 它允许您轻松地在项目中添加多种功能
  • 【网站】浏览器页面文本如何禁止和解除

    2023年 第37周 给自己一个目标 然后坚持总会有收货 不信你试试 有时候 看法的网站 网页文本内容希望不能被复制 那么就需要对浏览器网页进行一些限制 是一种网站开发中的保护措施 有时候 又希望能够复制网页的一些文本 发现被禁止 这就有了
  • 1015-O专题三

    1 题号 1015 O 2 题意 十进制转换二进制 十进制数在0 1000之间 3 解题思路 十进制数1000的二进制数为1111101000 共10位 所以定义数组内存11就可以 除2 取余数分别标记 输出时要将数组从后往前输出 4 感想
  • python对MP4文件的音轨读取和整合

    工作中 使用opencv对视频的人脸做处理 但是发现处理完成后得到的视频文件并没有声音 为此 作者采用以下办法解决 1 安装moviepy库 pip install moviepy 2 导入moviepy库 from moviepy edi
  • 1.3 安卓应用目录结构

    一 安卓应用视图 打开之前我们创建的安卓应用 HelloWorld 1 Project视图 安卓项目默认是Android视图 需要切换到Project视图 2 Package视图 切换到Package视图 3 Android视图 切换到An
  • 如何实现随机生成坐标点,并且使每个坐标点之间的距离大于某个距离?(用于散点图的绘制,进行数据的处理)

    背景 最近需要开发一个新需求 需要绘制一个随机生成数字的散点图 要求点与点的距离要大于某个特定值 解决思路 通过循环获取每个坐标点 每获取一个新的坐标点 都要与之前生成的坐标点进行对比 如果大于指定距离 则符合条件 退出循环 如果小于或等于
  • found input variables with inconsistene numbers of samples:[] 报错处理

    在用train text spilt进行机器学习的训练时候 出现了以下的报错 代码检查发现错误 train x train y test x test y train test split train x train y的行数不一致 应该改
  • 1分钟教你配置好你的python环境

    欢迎来到我们的系列博客 Python360全景 在这个系列中 我们将带领你从Python的基础知识开始 一步步深入到高级话题 帮助你掌握这门强大而灵活的编程语法 无论你是编程新手 还是有一定基础的开发者 这个系列都将提供你需要的知识和技能
  • 详解移植mjpg_streamer到arm板

    介绍 Mjpg streamer是一个开源软件 用于从webcam摄像头采集图像 把它们以流的形式通过基于ip的网络传输到浏览器如Firefox Cambozola VLC播放器 Windows的移动设备或者其他拥有浏览器的移动设备 mjp
  • 从0到1搭建自己的脚手架(java后端)

    一 脚手架是什么 脚手架是一种基础设施工具 用于快速生成项目的框架代码和文件结构 它是一种标准化的开发工具 使开发人员能够在项目的早期阶段快速搭建出一个具备基本功能和结构的系统 二 脚手架的意义 主流的微服务架构体系下很多公司会将原有的单体
  • SPSS 24安装后怎么打开的问题

    本人安装完spss 24之后打开发现还是需要许可证 再次输入完成就会全部关闭 解决方法 安装的步骤基本不会有问题 主要是针对出现安装完成 也填好许可证了的情况 可以通过下图对应的文件位置 双击打开 就可以使用了 安装包和教程可参考 链接 l
  • 多线程2(同步代码块+同步方法+同步锁+死锁)

    一 多线程同步 多线程的并发执行可以提高程序的效率 但是当多个线程去访问同一个资源时 有时也会引发一些安全性问题 例如 统计一个班上的学生人数时 学生有进有出会影响最终学生人数 为了解决这样的问题 需要实现多线程的同步 即限制某个资源在同一