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

这篇具有很好参考价值的文章主要介绍了【多线程】| 线程冲突解决方案。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

🦁 线程同步

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

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块里,提高速率。文章来源地址https://www.toymoban.com/news/detail-454914.html

到了这里,关于【多线程】| 线程冲突解决方案的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • git请求合并时出现冲突的解决方案

    请求合并时出现冲突,一般是有多人修改了同一个地方导致的,我们一般在本地解决好冲突后再上传到远端仓库,然后再次发起合并。 本流程适合无主分支权限的情况 在出现冲突的开发分支上解决流程: 1、先拉取主干分支 main到本地开发分支dev git pull origin main 2、借助工具

    2024年02月11日
    浏览(46)
  • mysql‘逻辑删除‘和‘唯一索引‘冲突的解决方案

    在user表中将name字段设置唯一索引,添加逻辑删除字段del_flag(1为删除,0为未删除)之后,将name=张四的字段删除,再添加一个name=张四的记录则会出现冲突 删除的时候将del_flag设置为null,未删除时候为0,冲突解决 在配置文件中这样配置:

    2024年02月10日
    浏览(33)
  • Three.js深度冲突(模型闪烁)与解决方案

    下面代码创建两个重合的矩形平面Mesh,通过浏览器预览,当你旋转三维场景的时候,你会发现模型渲染的时候产生闪烁。 这种现象,主要是两个Mesh重合,电脑GPU分不清谁在前谁在后,这种现象,可以称为深度冲突 Z-fighting 。 look 适当偏移,解决深度冲突,偏移尺寸相对模型

    2024年02月17日
    浏览(36)
  • 解决Hash(哈希表)冲突的四种方案

    参考鸣谢 解决哈希冲突必须知道的几种方法 小僵鱼 你还应该知道的哈希冲突解决策略 vivo互联网技术 解决哈希冲突的三种方法 kaleidoscopic 每日一题(哈希表及哈希冲突解决办法) 和笙 哈希是一种通过对数据进行压缩, 从而提高效率的一种解决方法 ,但由于哈希函数有限,数据

    2024年02月14日
    浏览(37)
  • Elasticsearch深入理解 并发写入导致版本冲突解决方案【实战】

         数据同步中,在使用阿里云Elasticsearch7.10.0版本的集群作为目标数据源时,在连续写入同一文档(document)出现版本冲突问题。 注意:以下所述均以阿里云7.10.0版本的Elasticsearch集群为前提(不同版本可能会稍有不同)       以生产环境的错误日志信息为例: ElasticsearchSta

    2023年04月18日
    浏览(34)
  • 【Maven】jar包冲突原因与最优解决方案

    【Maven】jar包冲突原因与 最优 解决方案 你是否经常遇到这样的报错: 以上报错就有可能是jar包冲突造成的,Maven中jar包冲突是开发过程中比较常见而又令人头疼的问题,我们需要知道 jar包冲突的原理,才能更好的去解决jar包冲突的问题。本文将从jar包冲突的原理和解决jar包

    2023年04月23日
    浏览(35)
  • 中间件多版本冲突的4种解决方案和我们的选择

    背景 在小小的公司里面,挖呀挖呀挖。最近又挖到坑里去了。一个稳定运行多年的应用,需要在里面支持多个版本的中间件客户端;而多个版本的客户端在一个应用里运行时会有同名类冲突的矛盾。在经过询问chatGPT,百度,google,github,和各位大佬的文章后,进行了总结。

    2024年02月13日
    浏览(37)
  • git提交到远程仓库 Git HEAD detached from origin(冲突解决后无法正常push) 问题的解决方案

    目录 一、Git提交代码的流程 二、将本地项目发布到gitee上 将本地代码库与远程代码库相关联 强制把远程仓库代码拉取到到当前分支上面。ps:如果仓库为空这一步可以跳过 三、git提交代码--修改提交的地址 四、获取密钥 Git HEAD detached from origin(冲突解决后无法正常push) 问题

    2024年02月03日
    浏览(57)
  • 【多线程】线程安全问题原因与解决方案

    目录 线程安全的概念 线程不安全示例 线程不安全的原因      多个线程修改了同一个变量     线程是抢占式执行的     原子性     内存可见性     有序性 线程不安全解决办法  synchronized -监视器锁monitor lock     synchronized 的特性         互斥         刷新内

    2024年02月06日
    浏览(33)
  • 【线程安全】死锁问题及解决方案

    比如上一次讲到 synchronized 的时候,一个线程,对同一个对象连续加锁两次,如果出现阻塞等待,代表这个锁是不可重入锁,这样的线程,也就称为死锁! 一旦程序进入死锁了就会导致线程僵住了,无法继续执行后续的工作了,程序也就出现了严重的 BUG! 而死锁这样的情况

    2024年02月06日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包