首页 > 其他分享 >线程中的虚假唤醒

线程中的虚假唤醒

时间:2024-08-27 15:55:40浏览次数:8  
标签:HashMap 虚假 number Collections 线程 new 唤醒

理论基础:线程间通信:

1、生产者+消费者

2、通知等待唤醒机制 wait 和notify

为什么一个关于线程的操作,方法却放在Object包下?

因为多线程的线程安全,必定依赖于锁,而任何对象都可以当锁对象,所以将公共的方法放入到Object类中。

多线程编程模板B: 判断 干活 通知

基于以上理论,我们写出第一版的代码(synchronized实现):

class ShareDataOne//资源类
{
  private int number = 0;//初始值为零的一个变量
 
  public synchronized void increment() throws InterruptedException 
  {
     //1判断
     if(number !=0 ) {
       this.wait();
     }
     //2干活
     ++number;
     System.out.println(Thread.currentThread().getName()+"\t"+number);
     //3通知
     this.notifyAll();
  }
  
  public synchronized void decrement() throws InterruptedException 
  {
     // 1判断
     if (number == 0) {
       this.wait();
     }
     // 2干活
     --number;
     System.out.println(Thread.currentThread().getName() + "\t" + number);
     // 3通知
     this.notifyAll();
  }
}
 
public class NotifyWaitDemoOne
{
  public static void main(String[] args)
  {
     ShareDataOne sd = new ShareDataOne();
     new Thread(() -> {
       for (int i = 1; i < 10; i++) {
          try {
            sd.increment();
          } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
          }
       }
     }, "A").start();
     new Thread(() -> {
       for (int i = 1; i < 10; i++) {
          try {
            sd.decrement();
          } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
          }
       }
     }, "B").start();
  }
}

结果异常的完美,但是如果启动的是四个线程呢?

导致出现线程混乱的原因是:虚假唤醒

出现虚假唤醒的原因:

If只判断一次,while可以判断多次,wait()被唤醒后直接在原地执行,所以会出现以上这种情况。

解决方案: if ==> while

中断和虚假唤醒是可能产生的,所以要用loop循环,if只判断一次,while是只要唤醒就要拉回来再判断一次。if换成while

将if判断更改为while判断即可解决虚假唤醒

解决原理

因为if只进行一次判断,假设当+1抢到了资源, 此时的num=1,其他三个线程进行资源抢夺,即使此时+1*抢到了资源,但是判断后,无法进行下面的代码,就沉睡下去,再当+1抢到资源时,就不会再从开始进行if判断了,从而会直接执行下面的++代码,使得num的值变为了2,导致线程不安全的情况,造成了虚假唤醒

添加一点关于线程安全的内容

我们常见的ArrayListHashSet, HashMap 的默认长度是多少,每次扩容多少?

ArrayList

  • 默认长度:10

  • ArrayList 默认长度是10 ,每次扩容50% ,底层使用的是Object[] 。

HashSet

  • 默认长度:16

  • 扩容方式:HashSet底层是基于HashMap实现的,因此其扩容机制与HashMap类似。当HashSet中的元素数量达到容量与加载因子(默认为0.75)的乘积时,HashSet会进行扩容。扩容时,容量也会增加为原来的1.5倍。

HashMap

  • 默认长度:16

  • 扩容方式:当HashMap中的元素数量达到容量与加载因子(默认为0.75)的乘积时,HashMap会进行扩容。扩容时,容量同样会增加为原来的1.5倍,并且所有键值对会重新哈希到新的位置上。

常见的集合ArrayList,HashSet, HashMap 他们是线程安全的吗?如果不安全,如何解决

       都是线程不安全的,可以使用

  1. 使用Vectory

  2. 使用List<String> list = Collections.synchronizedList(new ArrayList<>());

Collections提供了方法synchronizedList保证list是同步线程安全的。

Collections.synchronizedList()、Collections.synchronizedMap()、Collections.synchronizedSet()同步集合类完善线程安全

使用的是Collections工具类提供的内容。可以解决线程安全问题,但是效率不是特别高。

  1. 最终方案:

做法: list --> List<String> list = new CopyOnWriteArrayList();

Set -- > Set<String> set = new CopyOnWriteArraySet<>();

Map --> Map<String,String> map = new ConcurrentHashMap();

原理:使用了写时复制技术,速度要比同步锁快,类似于表锁和行锁的区别

CopyOnWriteArrayList是arraylist的一种线程安全变体,

多线程创建的四种方式:

  1. 实现runable接口,这是创建线程最传统的方式之一

  2. 继承thread类: 这是另一种创建线程的方式,通过继承Thread类并重写run()方法。

  3. 使用线程池

  4. 使用Future和Callable

class MyThread implements Runnable{
 @Override
 public void run() {
 
 }
}
新类MyThread2实现callable接口
class MyThread2 implements Callable<Integer>{
 @Override
 public Integer call() throws Exception {
  
  return 200;
 } 
}·

标签:HashMap,虚假,number,Collections,线程,new,唤醒
From: https://blog.csdn.net/qq_62984376/article/details/141604989

相关文章

  • 多线程-interrupt
    多线程-interrupt中断一个线程非常简单,只需要在其他线程中对目标线程调用interrupt()方法,目标线程需要反复检测自身状态是否是interrupted状态,如果是,就立刻结束运行。1、案例一packagecom.example.one;/***@authortom*/publicclassMain{publicstatic......
  • python aiohttp创建很多线程的问题及解决例子解析
    在使用aiohttp进行异步HTTP请求时,创建大量线程可能会导致性能问题。根据搜索结果,这个问题通常与DNS查询有关,因为默认情况下,每次发送请求时aiohttp.ClientSession都会进行DNS查询,这是一个阻塞操作,会为每次查询创建一个新线程。为了解决这个问题,可以通过指定一个AsyncR......
  • C#上位机开发——多线程启动停止暂停继续
    引用:上位机开发——多线程启动停止暂停继续-哔哩哔哩(bilibili.com)前言初学者学习编程时,很容易因为多线程出现各种问题,导致不敢使用多线程。但是多线程技术在做开发中,是不可忽视的一个技术,基本上我们实际应用中,每个项目都会使用多线程,所以多线程技术必须掌握。为什么要用多......
  • Java线程的实践及原理揭秘
    Java线程的实践及原理揭秘并发是什么?系统支持高并发的因素是哪些?1.如何理解系统的并发一般来说,系统在单位时间内能够承载的并发数就是整个系统同事能够处理的请求数量。对于并发的指标通常通过TPS/QPS来表示QPS:每秒处理的查询数(Queries-Per-Second)TPS:每秒处理的事务数(Tr......
  • java 线程
    1.Java中有哪几种方式来创建线程执行任务1.继承Thread类(单继承)2.Runnable接口(没有继承限制)但是无法返回值3.callable接口结合FutureTask4.利用线程池来创建线程使用ExecutorService调用execute通过runnable创建底层都是基于runnable2.为什么不建议使用Executors来创建......
  • 操作系统终止线程
    终止线程方法1:从线程入口函数中return,主线程除外。方法2:调用pthread_exit函数。voidpthread_exit(void*retval);retval-和线程过程函数的返回值语义相同。注意:在任何线程中调用exit函数都将终止整个进程。问题:主线程结束,子线程是否会跟着一起结束?主线程结束,并不会......
  • 一次性下发100w的优惠券/短信/二维码,兼顾线程池参数可配置 在Spring 中 ThreadPoolTas
    一次性下发100w的优惠券/短信/二维码,兼顾线程池参数可配置在Spring中ThreadPoolTaskExecutor的使用1、场景需求分析针对6.18,11.11这种场景,平台一次性发布500w张优惠券,或者对于锁单用户统一发下100w张确认信息,同时我们平时有抢购茅台的场景,京东一次性发布10w个验证码,主要是针......
  • Python 多线程编程技巧举例
    Python多线程(Multithreading)是一种编程技术,允许在同一程序中同时执行多个独立的逻辑流,即线程。每个线程都有自己的程序计数器、栈空间和局部变量,它们共享同一进程的全局变量、文件描述符和其他系统资源。线程是操作系统调度的基本单位,能够在单个进程中并发运行,从而实现任务......
  • B站宋红康JAVA基础视频教程个人笔记chapter08-09(异常处理+多线程)
    文章目录1.异常处理方式1:try-catch-finally2.异常处理方式1:throws3.程序,进程,线程的区别4.线程的创建4.1线程的创建方式1:4.2线程的创建方式2:5.线程类的常用方法和生命周期5.1线程的生命周期jdk5之前6.线程的安全问题和同步机制6.线程之间的通信6.1为什么需要线程之间......
  • 解读GaussDB(for MySQL)表级恢复,看线程数及分块分行策略如何提升恢复性能?
    本文分享自华为云社区《【华为云MySQL技术专栏】GaussDB(forMySQL)表级恢复中mydumper、myloader的应用与性能优化》,作者:GaussDB数据库。 背景介绍表级时间点恢复技术为“误删表”场景提供了一种快速且精确的恢复方案。通过将指定时间点的数据恢复到临时实例,再把用户所需的......