【多线程】| 线程冲突解决方案

news2024/11/19 22:37:42

目录

  • 🦁 线程同步
    • 1.什么是线程冲突?
    • 2.什么是线程同步?
    • 3.解决线程同步的方案
      • 3.1语法结构
      • 3.2synchronized使用
  • 🦁 synchronized详细用法
    • 1. 使用this作为线程锁对象
      • 1.1 语法结构:
      • 1.2 使用说明
    • 2. 使用字符串作为线程对象锁
      • 2.1 语法结构
      • 2.2 使用说明
    • 3. 使用Class作为线程对象锁
      • 3.1 语法结构
      • 3.2 使用说明
    • 4. 使用自定义对象作为线程对象锁
      • 4.1 语法结构
      • 4.2 使用说明
  • 🦁 conclusion

🦁 线程同步

在这里插入图片描述

1.什么是线程冲突?

同一进程内的线程是共享同一内存空间的,所以在多个线程的进程里,线程是可以同时操作这个进程空间的数据的,这样就容易造成线程冲突的情况。

举个小李子:一个房子里(代表一个进程),只有一个厕所(代表一个资源)。屋子里面有两个人A和B(代表两个线程),共用这个厕所。一天A去上厕所了,一上就仨小时。到第三个小时的时候,B实在忍不住了,直接就开门进去把在里面玩手机的A轰了出来。然后A就被赶出来了,嘴里骂骂咧咧……

这里说明了什么呢???我们前面说了进程间线程是按照时间片轮询的方式执行的,A执行了三个小时,换到B,可是A还没有执行完成啊!!!厕所里面的数据还在运作,就被B给搅浑了。。。。。那怎么成(虽说A不厚道)!!!这就是线程冲突

如何来解决这个问题呢????(后面揭晓!)

只需记住:“将并发任务转为串行任务就迎刃而解了

2.什么是线程同步?

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

按这个说法,只要A能让B持续在厕所外面等待A并且即使有想冲进去的想法也奈何不了A的时候,是不是就解决了这个冲突???

那么A是不是把门给锁上就好了?只要A还没执行完,B就需要在外面等待(你看,这不就是并行变串行?)。。。(现实生活不建议,缺德。。)

3.解决线程同步的方案

那么在Java中,我们如何实现这个同步呢???

Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问造成的这种问题。这套机制就是synchronized关键字

3.1语法结构

synchronized(锁对象){ 
   同步代码
 }

右三部分组成,synchronized、锁对象、{}。其中

锁对象:决定了让哪些线程有互斥的效果。

3.2synchronized使用

它有两种用法:

  • 要么加在方法的前面(方法声明)
  • 要么写成一个代码块(回想static代码块)
  1. 方法声明:放在访问控制符(public)之前或之后。(不建议)
public  synchronized  void accessVal(int newVal);
  1. synchronized块:这里还是以前面的例子为例,厕所是A和B同时操作的对象,所以synchronized就应该以厕所为锁对象。
synchronized(W.C.){
	doSomething();
}

tips:

若将一个大的方法声明为synchronized 将会大大影响效率。synchronized 块可以让我们精确地控制到具体的“成员变量”,缩小同步的范围,提高效率

🦁 synchronized详细用法

现在来看看synchronized锁对象还能如何定义?

1. 使用this作为线程锁对象

在所有线程中相同对象中的synchronized会互斥

1.1 语法结构:

有两种:

synchronized(this){ 
    //同步代码 
   }
public synchronized void accessVal(int newVal){

//同步代码

}

1.2 使用说明

/**
 * 定义程序员类
 */
class Programmer{
  private String name;
  public Programmer(String name){
    this.name = name;
   }
  /**
   * 打开电脑
   */
   public void computer(){
     synchronized(this){
      try {
        System.out.println(this.name + " 接通电源");
        Thread.sleep(500);
        System.out.println(this.name + " 按开机按键");
        Thread.sleep(500);
        System.out.println(this.name + " 系统启动中");
        Thread.sleep(500);
        System.out.println(this.name + " 系统启动成功");
       } catch (InterruptedException e) {
        e.printStackTrace();
       }   
     }
   }
  /**
   * 编码
   */
  synchronized public void coding(){
      try {
        System.out.println(this.name + " 双击Idea");
        Thread.sleep(500);
        System.out.println(this.name + " Idea启动完毕");
        Thread.sleep(500);
        System.out.println(this.name + " 开开心心的写代码");
       } catch (InterruptedException e) {
        e.printStackTrace();
       }
     }
}


/**
 * 打开电脑的工作线程
 */
class Working1 extends Thread{
  private Programmer p;
  public Working1(Programmer p){
    this.p = p;
   }
  @Override
  public void run() {
    this.p.computer();
   }
}


/**
 * 编写代码的工作线程
 */
class Working2 extends Thread{
  private Programmer p;
  public Working2(Programmer p){
    this.p = p;
   }
  @Override
  public void run() {
    this.p.coding();
   }
}
public class TestSyncThread {
  public static void main(String[] args) {
    Programmer p = new Programmer("Lion");
    new Working1(p).start();
    new Working2(p).start();
   }
}

这里程序员类中如果不加synchronized(this),那么就会测试类中两个线程就会并发执行,又开电脑,又开IDEA,这是不可能的,必须先打开电脑再打开IDEA。加入synchronized(this)就代表了当前对象如果是同一个对象,则会将并发变为串行

2. 使用字符串作为线程对象锁

所有线程在执行synchronized时都会同步。(也就是不同的对象也会进行线程互斥)

2.1 语法结构

synchronized(“字符串”){ 
    //同步代码 
   }

这里的字符串可以任意填写,空串也行。。。

2.2 使用说明

我们依然引用上面的例子,在程序员类添加一个方法,如下:

 /**
   * 去卫生间
   */
  public void wc(){
    synchronized ("t") {
      try {
        System.out.println(this.name + " 打开卫生间门");
        Thread.sleep(500);
        System.out.println(this.name + " 开始排泄");
        Thread.sleep(500);
        System.out.println(this.name + " 冲水");
        Thread.sleep(500);
        System.out.println(this.name + " 离开卫生间");
       } catch (InterruptedException e) {
        e.printStackTrace();
       }
     }
   }
}

/**
 * 去卫生间的线程
 */
class WC extends Thread{
  private Programmer p;
  public WC(Programmer p){
    this.p = p;
   }
  @Override
  public void run() {
    this.p.wc();
   }
}

我们在测试的时候,创建两个不同的程序员

public class TestSyncThread {
  public static void main(String[] args) {
    Programmer p1 = new Programmer("A");
    Programmer p2 = new Programmer("B");
    new WC(p1).start();
    new WC(p2).start();
   }
}

这时候他们并不会一起上厕所,而是一个上完另一个才去上。

3. 使用Class作为线程对象锁

tips:

此Class非彼class!!!——>前者是一个类,后者是Java的一个关键字(有兴趣可以去查查看,没骗你哦!)

**Class 类型的对象是 Java 中的一个特殊对象,每个类在 JVM 中都有一个对应的 Class 对象,用于描述该类的结构。**当我们使用 Class 对象作为锁时,可以保证同一时刻只有一个线程可以访问该 Class 类型对象所同步的代码块.

在所有线程中,拥有相同Class对象中的synchronized会互斥

3.1 语法结构

// 推荐
synchronized(XX.class){ 
    //同步代码 
   }

跟前面不一样,这里的synchronized加在静态方法的前面,表示它所在类以class模板作为线程对象锁,因为静态方法不能使用this

synchronized public static void accessVal()

3.2 使用说明

场景:同一部门的员工去领奖金互斥,不同部门的员工去领奖金并行。

还是复用上面的代码,添加一个销售部门,并且都添加一个领取奖金的方法(领奖金都是一个一个领的,不会一群人同时领)和相关的线程类。

/**
 * 定义销售员工类
 */
class Sale{
  private String name;
  public Sale(String name){
    this.name = name;
   }
  /**
   * 领取奖金
   */
   public void money(){
      synchronized(Sale.class){
       try {
        System.out.println(Thread.currentThread().getName() + " 被领导表扬");
        Thread.sleep(500);
        System.out.println(Thread.currentThread().getName() + " 拿钱");
        Thread.sleep(500);
        System.out.println(Thread.currentThread().getName() + " 对公司表示感谢");
        Thread.sleep(500);
        System.out.println(Thread.currentThread().getName() + " 开开心心的拿钱走人");
       } catch (InterruptedException e) {
        e.printStackTrace();
       }
     }
   }
}
// 在程序员的代码里面也添加同样的方法表示领取奖金。这里使用不一样的写法
/**
   * 领取奖金
   */
   public synchronized static void money(){
       try {
        System.out.println(Thread.currentThread().getName() + " 被领导表扬");
        Thread.sleep(500);
        System.out.println(Thread.currentThread().getName() + " 拿钱");
        Thread.sleep(500);
        System.out.println(Thread.currentThread().getName() + " 对公司表示感谢");
        Thread.sleep(500);
        System.out.println(Thread.currentThread().getName() + " 开开心心的拿钱走人");
       } catch (InterruptedException e) {
        e.printStackTrace();
       }
     }
}


/**
 * 程序员领取奖金线程
 */
class ProgrammerMoney extends Thread{
  private Programmer p;
  public ProgrammerMoney(Programmer p){
    this.p = p;
   }
  @Override
  public void run() {
    this.p.money();
   }
}

/**
 * 销售部门领取奖金线程
 */
class SaleMoney extends Thread{
  private Sale p;
  public SaleMoneyThread(Sale p){
    this.p = p;
   }
  @Override
  public void run() {
    this.p.money();
   }
}

测试:

public class TestSyncThread {
  public static void main(String[] args) {
    Programmer p = new Programmer("张三");
    Programmer p1 = new Programmer("李四");
    new ProgrammerMoney(p).start();
    new ProgrammerMoney(p1).start();
    Sale s = new Sale("张晓丽");
    Sale s1 = new Sale("王晓红");
    new SaleMoney(s).start();
    new SaleMoney(s1).start();
   }
}

可以发现:p和p1互斥,s和s1互斥,但是p类型的对象和s类型的对象并发进行。

tips:

同一时刻只有一个线程可以运行 run() 方法中 synchronized 块中的代码

需要注意的是,在使用 Class 对象作为线程对象锁时,需要确保所有线程都使用同一个 Class 对象锁,否则无法保证同步性

4. 使用自定义对象作为线程对象锁

在所有线程中,拥有相同自定义对象中的synchronized会互斥

4.1 语法结构

只有一种结构

synchronized(自定义对象){ 
    //同步代码 
}

4.2 使用说明

public class MyClass {
    private final Object lock = new Object();

    public void myMethod() {
        synchronized (lock) {
            // 同步块中的代码
        }
    }

    public static void main(String[] args) {
        MyClass obj = new MyClass();

        Thread thread1 = new Thread(new MyThread(obj));
        Thread thread2 = new Thread(new MyThread(obj));

        thread1.start();
        thread2.start();

        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class MyThread implements Runnable {
    private MyClass obj;

    public MyThread(MyClass obj) {
        this.obj = obj;
    }

    public void run() {
        obj.myMethod();
    }
}

在上面的代码中,我们定义了一个 MyClass 类,其中包括一个 myMethod() 方法和一个 lock 对象,lock 对象作为自定义对象锁。

在 myMethod() 方法中,我们使用 synchronized 关键字来实现同步,将 lock 对象作为同步锁。在 main() 方法中,我们创建了两个线程并启动它们,这两个线程共享的锁对象是 MyClass 实例中的 lock 对象,因此在同一时刻只有一个线程可以运行 myMethod() 方法中 synchronized 块中的代码。

需要注意的是,使用自定义对象作为线程对象锁时,需要确保所有线程都使用同一个锁对象,否则无法保证同步性。此外,为了避免出现死锁情况,同步块中的代码应该尽量简短,以免占用锁时间过长。

🦁 conclusion

使用synchronized处理线程同步时,有四种锁对象类型

  1. this关键字,代表当前对象,使用它表示在所有线程中,同一个对象执行的不同操作会发生互斥(同时打开电脑和打开IDEA)
  2. 字符串:表示不同线程在执行同一个操作时,也会发生互斥
  3. Class类对象模板:表示使用同一对象模板的实例执行某一操作时会发生互斥,不同对象模板实例执行同一操作还是会并发。
  4. 自定义对象:表示线程间共享的锁对象是同一个自定义对象时,会发生互斥。

tips:
使用synchronized时,最好还是写成synchronized块的形式,把主要互斥的代码放到synchronized块里,提高速率。

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

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

相关文章

lwIP更新记04:TCP 初始序列号

从 lwIP-2.0.0 开始,可以自定义 TCP 报文段的初始序列号。 TCP 报文段首部有一个序列号字段,它是一个32位的计数器,从 0 到 4294967295,它的值为当前报文段中第一个数据的字节序号。TCP 在建立连接的时候需要初始序列号&#xff…

JVM系列-第11章-垃圾回收相关概念

垃圾回收相关概念 System.gc() 的理解 在默认情况下,通过System.gc()者Runtime.getRuntime().gc() 的调用,会显式触发Full GC,同时对老年代和新生代进行回收,尝试释放被丢弃对象占用的内存。 然而System.gc()调用附带一个免责声…

小航助学2022年NOC初赛图形化(小高组)(含题库答题软件账号)

需要在线模拟训练的题库账号请点击 小航助学编程在线模拟试卷系统(含题库答题软件账号)_程序猿下山的博客-CSDN博客 单选题3.0分 删除编辑 答案:C 第1题如果要控制所有角色一起朝舞台区右侧移动,下面哪个积木块是不需要的? A…

微签助力中融基金电子文件安全高效签章

中融基金重安全,炼丹炉里炼微签 这次讲一个微签在炼丹炉里炼出了火眼金睛的故事。 先看一个数字。 金融隐私泄露事件大约以每年35%的数据在增长。 数字来自《中国银行保险报》与亚信网络安全产业技术研究院发布的《金融行业网络安全白皮书》。 大数据时…

「车型分析」控制系统典型应用车型 —— 辊筒AGV

辊筒AGV (Roller conveyor ) 是一种常见的AGV机器人类型,它利用辊筒和轮子在巷道中实现货物的搬运和运输,可实现托盘物品的卸载和运输等功能, 具有更高的灵活性、适应性和效率。本文将基于这款市场上常见的AGV进行一次简单的介绍。 1 车型介绍: 辊筒AGV…

深度学习中必备的算法:神经网络、卷积神经网络、循环神经网络

深度学习是一种新兴的技术,已经在许多领域中得到广泛的应用,如计算机视觉、自然语言处理、语音识别等。在深度学习中,算法是实现任务的核心,因此深度学习必备算法的学习和理解是非常重要的。 本文将详细介绍深度学习中必备的算法…

2023彩虹易支付最新原版安装教程(内附源码)

此源码已通过检查,确认无后门,且所有代码开源,无加密文件。 测试日期 2023年5月21日 源码已扫描无后门,不放心的也可以自己再去扫描一遍 2023年5月22日 各个功能接口测试完毕,均可用 选中下方可查看下载链接 http…

gcc、makefile和git(二)

简介 现在讲述如何写 makefile 的文章很多,从几页的简易版到几十页、几百页的详细版都有。Makefile 有自己的书写格式、关键字、函数、隐含规则,像C语言一样灵活多变、功能强大。在Makefile中还可以使用系统shell所提供的任何命令来完成想要的工作&#…

国资油企能向民营市场借鉴些什么?

在中国石油集团召开的全面深化改革领导小组第四十三次会议上,集团公司董事长、党组书记、全面深化改革领导小组组长戴厚良强调:要深入推进公司治理体系和治理能力现代化,推进信息化、数字化和智能化工作。实现制度流程化、流程信息化。立足长…

GEE土地利用产品汇总

一、ESA产品: 10米分辨率,目前只有2020年和2021年的数据 (1)ESA WorldCover 10m v100(只有2020年数据) 代码: var dataset ee.ImageCollection(ESA/WorldCover/v100).first();var visualizat…

写给Android工程师的 Github CI 快速指北

背景 关于 CI/CD ,在2023年的今天,基本所有技术团队或多或少都会使用,其很大程度上减轻了我们的冗余重复工作,从而简化我们的工作流程。 不过对于大多数客户端工程师而言,其实 CI 这个词还是比较陌生。当然并不是说&…

Unity - 解决TMP FontAssetCreator 在生成 SDF 时 Font 显示 ????? 的问题

文章目录 原因问题解决下载 FontCreator,并打开有问题的字体修改字体属性重新导出字体 返回 Unity 后重新使用 TMP Font Asset Creator 来生成 原因 美术找到一个字体和某个参考的某个游戏的字体是一致 美术同学截图了参考的游戏,和 自己找到的 字体 放…

Mybatis查询语句汇总与小技巧

前言 在之前的学习中,使用到了将查询结果封装到bean对象内,使用到了将查询结果封装的bean对象封装到List集合中,但是如果我们没有对应的bean对象,我们应该将结果封装到哪呢?今天这篇文章就记录一下几种常见查询结果的封…

用蹩脚英语在StackOverflow上飞奔:试看以色列兄弟自荐的Http文件上传工具MgntUtils

上班摸鱼、下班干活,日常埋坑、加班填坑——这是我的搬砖,亦是在座的各位! 文章目录 1.试看MgntUtils来源2.下载MgntUtils源码3.初探源码4.验证API 1.试看MgntUtils来源 ​ ​上篇文章说到,有个以色列大兄弟在回答Stack Overflow…

13岁青少年DAO创始人:Web3治好了我的“丧”

“我看大家都死气沉沉的,大家都站起来活动活动。” 4月,香港Web3嘉年华的一场沙龙,橙色针织帽给黑压压的现场带来一抹亮色,13岁的Carry Zheng戴着它登台,没有“大家好”的寒暄,直接向台下的成年人发出指令&…

训练营-5月

JAVA训练营-5月 一、环境 1、idea安装 idea就是一个开发工具,写代码的地方 目录结构: --- 项目(工程 project) 比如:京东 ---- 模块(module) 比如:订单、购物车、秒杀等 -----…

ALOHA 开源机械臂(Viper 300 Widow X 250 6DOF机械臂 远程操控系统)第四部分

Teleoperation System 远程操作系统 We introduce ALOHA: A Low-cost Open-source Hardware System for Bimanual Teleoperation. With a $20k budget, it is capable of teleoperating precise tasks such as threading a zip tie, dynamic tasks such as juggling a ping p…

【2023 · CANN训练营第一季】进阶班 应用开发深入讲解→端到端案例

1 样例调试 1.1 日志文件 运行应用程序后,若出现报错或异常,需录取日志进一步定位问题。日志文件的默认目录为$HOME/ascend/log。 可通过环境变量指定日志文件的落盘路径 export ASCEND_PROCESS_LOG_PATH/$HOME/xxx但需要确保该目录为任意有读写权限…

外参手算方法

虽然有的slam系统是代外参标定功能,可以在线标定(vins)或者离线进行标定,但外参标定的质量也会与运动激励相关的,例如对于3自由度的小车很难把z方向的外参标定的很好。有些情况车子或者是定位模块是有设计图纸的&#…

Ubuntu22.04下使用Conda安装PyTorch GPU版本

环境 首先,你需要有 GPU 支持。 Ubuntu 22.04 显卡 $ nvidia-smi Mon May 22 11:15:33 2023 --------------------------------------------------------------------------------------- | NVIDIA-SMI 530.30.02 Driver Version: 530.30.02 CUDA…