ThreadLocal原理、结构、源码解析

news2024/11/18 20:35:45

文章目录

    • 一、Thread简介
      • 1.什么是ThreadLocal
      • 2.为什么要是用ThreadLocal
        • 2.1Synchronized、Lock保证线程安全
        • 2.2ThreadLocal保证线程安全
      • 3.ThreadLocal和Synchronized的区别
    • 二、ThreadLocal原理
      • 1.Thread抽象内部结构
      • 2.ThreadLocal源码
        • 2.1Thread、ThreadLocal、ThreadLocalMap、Entry之间关系
        • 2.2ThreadLocal类的set()方法
        • 2.3ThreadLocal类的get()方法
        • 2.4ThreadLocal类的remove()方法
        • 2.5面试:说一说ThreadLocal原理、Thread如何实现线程隔离的
    • 三、深究ThreadLocal
      • 1.为什么不直接用线程id作为ThreadLocalMap的key呢?
      • 2.ThreadLocal导致内存泄漏的原因
      • 3.ThreadLocalMap在设计中有没有考虑到内存泄漏这点呢?
      • 4.在使用ThreadLocal中,我们应该注意什么
      • 5.key是弱引用,GC回收会影响ThreadLocal的正常工作嘛
      • 6.Entry的key为什么要设计成弱引用呢,为什么不使用强引用
        • 6.1Entry的key如果使用强引用
        • 6.2为什么要设计成弱引用
        • 6.3四种引用类型
      • 7.我们希望父子线程之间共享数据,应该怎么做呢
        • 7.1InheritableThreadLocal
        • 7.2InheritableThreadLocal是如何实现父子线程之间共享的

一、Thread简介

1.什么是ThreadLocal

ThreadLocal,即线程本地变量。如果你创建了一个ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的一个本地拷贝,多个线程操作这个变量的时候,实际是在操作自己本地内存里面的变量,从而起到线程隔离的作用,保证了线程安全

  • 因为每个 Thread 内有自己的实例副本,且该副本只能由当前 Thread 使用。这是也是 ThreadLocal 命名的由来。
  • 每个 Thread 有自己的实例副本,且其它 Thread 不可访问,那就不存在多线程间共享的问题

2.为什么要是用ThreadLocal

保证线程安全,并发场景下,会出现多个线程共享一个变量的场景,这种场景可能会出现线程安全性问题,我们可以采取加锁的方式(Synchronized、Lock)方式,也可以使用使用ThreadLocal方式来避免线程安全问题。

2.1Synchronized、Lock保证线程安全

采用加锁方式保证线程安全到导致系统变慢。共享变量某个时刻只能由一个线程访问,其他线程需要等到该线程释放锁才能访问,影响系统性能。


2.2ThreadLocal保证线程安全

使用ThreadLocal。使用ThreadLocal类访问共享变量时,会在每个线程的本地,都保存一份共享变量的拷贝副本。多线程对共享变量修改时,实际上操作的是这个变量副本,从而保证线性安全。

3.ThreadLocal和Synchronized的区别

  • ThreadLocal和Synchronized都是为了解决并发问题。
  • Synchronized用于线程共享, 而ThreadLocal用于线程隔离
  • Synchronized是时间换空间,ThreadLocal是空间换时间
  • Synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。

二、ThreadLocal原理

1.Thread抽象内部结构

简单理解(看不懂先往下看再回顾):

  • Thread类中有ThreadLocal.ThreadLocalMap threadLocals属性
  • ThreadLocalMap 是Thread的静态内部类,它维护着Entry对象数组,每个Entry代表一个ThreadLocalMap对象
  • Entry是ThreadLocalMap的静态内部类,存储采用的是k-v形式,key为ThreadLocal,value为我



2.ThreadLocal源码

2.1Thread、ThreadLocal、ThreadLocalMap、Entry之间关系

先根据源码砍砍他们之间的关系

  • Thread的成员变量ThreadLocal.ThreadLocalMap
  • ThreadLocal.ThreadLocalMap是ThreadLocal的静态内部类
  • Entry是ThreadLocalMap的静态内部类,Entry继承了弱引用,也就是说,如果外部没有强引用关联的话,下一次GC时会被回收。
  • 在Entry是一个k-v形式,内部使用ThreadLocal作为key,使用我们设置的value作为value。
  • Entry[] table为ThreadLocal的属性,由ThreadLocalMap维护
//Thread类
class Thread implements Runnable {
    //ThreadLocalMap是Thread的成员变量
    ThreadLocal.ThreadLocalMap threadLocals = null;//ThreadLocal类       
public class ThreadLocal<T> {
    
       //Entry数组是ThreadLocal的成员变量,该数组由ThreadLocalMap维护
        private Entry[] table;
    
//静态内部类ThreadLocalMap
static class ThreadLocalMap {

       //Entry是ThreadLocalMap的静态内部类,注意,Entry继承了弱引用(如果没有被强引用关联,下一次GC会被回收)
        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
}
}
        

2.2ThreadLocal类的set()方法

通过源码可以看到,ThreadLocalMap维护了Entry数组,Entry是k-v形式,key为ThreadLocal,value为我们传入的值,当同一个线程对同一个ThreadLocal进行两次set时,value会被覆盖。

  1. 获取当前线程,根据当前线程获取ThreadLocalMap
  2. 判断ThreadLocalMap是否存在
  3. 存在则将ThreadLocal作为key,传入的值为value,存入ThreadLocalMap的Entry中
  4. 不存在则根据ThreadLocalMap构造函数创建ThreadLocalMap并将ThreadLocal作为key,传入的值为value,存入ThreadLocalMap的Entry中
    public void set(T value) {
        Thread t = Thread.currentThread();//获取当前线程t
        ThreadLocalMap map = getMap(t);//根据当前线程获取到ThreadLocalMap
        if (map != null)
            map.set(this, value);//如果map不为空,则将当前对象ThreadLocal作为key,传入的值为value存入ThreadLocalMap中
        else
            createMap(t, value);//如果map为空,则创建ThreadLocalMap对象后,再将k-v存入ThreadLocalMap中
    }

   //该方法位于ThreadLocal中
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;//根据线程t获取Thrad的ThreadLocalMap
    }

     //该方法位于ThreadLocal中
     void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);//调用ThreadLocalMap构造函数,this表示当前类ThreadLocal
    }

      //该方法位于ThreadLocal中,该方法位于ThreadLocal中构造函数维护这Entry数组table
      ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
            table = new Entry[INITIAL_CAPACITY];
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }



2.3ThreadLocal类的get()方法

  1. 获取当前线程t
  2. 根据线程t获取ThradLocalMap map
  3. map存在则获取Entry,Entry存在获取value
  4. map不存在,初始化ThradLocalMap并将ThreadLocal作为kay,valeu为null存进ThreadLocalMap中,返回value也就是null。
  5. (调用get()方法时,ThreadLocalMap没有初始化则会初始化并ThreadLocal作为kay,valeu为null存进ThreadLocalMap中)
public T get() {
        Thread t = Thread.currentThread();//获取当前线程
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);//map不为空,获取Entry
            if (e != null) {//entry不为空。返回value
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();//map为空,初始化threadLocals成员变量的值,也就是初始化把thread Local为key,value=null塞进entry
           
    }

     private T setInitialValue() {
        T value = initialValue(); //初始化value的值
        Thread t = Thread.currentThread(); 
        ThreadLocalMap map = getMap(t); //以当前线程为key,获取threadLocals成员变量,它是一个ThreadLocalMap
        if (map != null)
            map.set(this, value);  //K,V设置到ThreadLocalMap中
        else
            createMap(t, value); //实例化threadLocals成员变量
        return value;//返回null
    }

  protected T initialValue() {
        return null;
    }


2.4ThreadLocal类的remove()方法

  1. 获取当前线程,根据当前线程获取ThreadLocalMap
  2. 如果map部位空,则删除map中指定的的Entry
public void remove() {
    ThreadLocalMap m = getMap(Thread.currentThread());//获取当前线程的ThreadLocalMap变量
    if (m != null)
        m.remove(this);//对象不为空,则删除
}


ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}
private void remove(ThreadLocal<?> key) {
    Entry[] tab = table;
    int len = tab.length;
    int i = key.threadLocalHashCode & (len-1);
    for (Entry e = tab[i];
         e != null;
         e = tab[i = nextIndex(i, len)]) {
        if (e.get() == key) {
            e.clear();
            expungeStaleEntry(i);
            return;
        }
    }
}


2.5面试:说一说ThreadLocal原理、Thread如何实现线程隔离的

ThreadLocal叫本地线程变量,作用就是当多线程访问共享变量时,起到线程隔离的作用,每个线程都有自己的一个副本,且之间不被共享,这种方式采用的是空间换时间的方式。目的是保证线程安全采用空间换时间的方式保证线程安全。

每个线程Thread都有一个成员变量ThreadLocal.ThreadLocalMap thradLocals。也就是说每个线程都有一个ThreadLocalMap,这个ThreadLocalMap是ThreadLocal的静态内部类,他维护者Entry对象数组,Entry对象存储方式是k-v的形式。k为ThreadLocal,V为我我们set进去的值。

在并发场景下,每个线程在往ThreadLocal里面设置值得时候,实际就是存进自己的thradLocals属性中,以ThreadLocal为key,set进去的值为value。实现线程隔离


三、深究ThreadLocal

1.为什么不直接用线程id作为ThreadLocalMap的key呢?

ThreadLocalMap是Thread的属性,维护着Entry数组,Entry的key是ThradLocal,value为我们set入的值。

如果将线程id作为key,那么当一个Thread有多个ThreadLocal进行set()的时候,无法区分value是哪个ThreadLocal的,或者说无论多少个ThreadLocal,每次set进去,由于key都是ThreadId,会导致每次set都会被覆盖。

如图所示,一个key对应多个ThreadLocal。ThreadLocal-1 set()的时候key为Thread,ThreadLocal-1 set()的时候key依然为Thread

image-20230225181347271

2.ThreadLocal导致内存泄漏的原因

ThreadLocal导致内存泄漏愿意你有两个

  1. 使用完后没有remove()由于ThreadLocalMap 的生命周期跟 Thread 一样长,对于重复利用的线程来说(例如核心线程池中的线程),如果没有手动删除(调用remove()方法),会导致Entry对象越来越多,从而导致内存泄漏.
  2. 第二种原因下面讲解

Entry类继承了弱引用,super(k);使ThreadLocal也是一个弱引用

static class Entry extends WeakReference<ThreadLocal<?>> {
    /** The value associated with this ThreadLocal. */
    Object value;
    Entry(ThreadLocal<?> k, Object v) {
        super(k);
        value = v;
    }
}

ThreadLocal引用示意图

image-20230225183419928

  • ThreadLocalMap使用ThreadLocal的弱引用作为key,当ThreadLocal变量被手动设置为null,即一个ThreadLocal没有外部强引用来引用它,当系统GC时,ThreadLocal一定会被回收。
  • 这样的话,ThreadLocalMap中就会出现key为null的Entry,就没有办法访问这些key为null的Entry的value
  • 如果当前线程再迟迟不结束的话(比如线程池的核心线程),这些key为null的Entry的value就会一直存在一条强引用链:Thread变量 -> Thread对象 -> ThreaLocalMap -> Entry -> value -> Object 永远无法回收,造成内存泄漏。

当Thread手动设置为null后的因用链

image-20230225184237427

此时,堆中的ThreadLocal只存在弱引用,再下一次GC时会被回收。回收后,Entry中的key=null,这个Entry就没有办法被访问。导致内存泄漏。


3.ThreadLocalMap在设计中有没有考虑到内存泄漏这点呢?

实际上ThreadLocalMap在设计的时候就考虑到这个情况了,所以ThreadLocal的get、set方法中加了一些防护措施。在执行set、get方法的时候,会清楚线程中ThreadLocalMap中key为null的Entry。

ThreadLocal的set()方法防止内存泄漏措施

  private void set(ThreadLocal<?> key, Object value) {

            // We don't use a fast path as with get() because it is at
            // least as common to use set() to create new entries as
            // it is to replace existing ones, in which case, a fast
            // path would fail more often than not.

            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);

            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                ThreadLocal<?> k = e.get();

                if (k == key) {
                    e.value = value;
                    return;
                }

                if (k == null) {
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }

            tab[i] = new Entry(key, value);
            int sz = ++size;
      //触发一次Log2(N)复杂度的扫描,目的是清除过期Entry  
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }

ThreadLocal的get()方法防止内存泄露措施

public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
             //去ThreadLocalMap获取Entry,方法里面有key==null的清除逻辑
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }


        private Entry getEntry(ThreadLocal<?> key) {
            int i = key.threadLocalHashCode & (table.length - 1);
            Entry e = table[i];
            if (e != null && e.get() == key)
                return e;
            else
                //其中有key为null的清楚逻辑
                return getEntryAfterMiss(key, i, e);
        }


        private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
            Entry[] tab = table;
            int len = tab.length;

            while (e != null) {
                ThreadLocal<?> k = e.get();
                if (k == key)
                    return e;
                if (k == null)
                    // Entry的key为null,则表明没有外部引用,且被GC回收,是一个过期Entry
                    expungeStaleEntry(i);
                else
                    i = nextIndex(i, len);
                e = tab[i];
            }
            return null;
        }

4.在使用ThreadLocal中,我们应该注意什么

  • 将ThreadLocal变量定义成private static的,这样的话ThreadLocal的生命周期就更长,由于一直存在ThreadLocal的强引用,所以ThreadLocal也就不会被回收,也就能保证任何时候都能根据ThreadLocal的弱引用访问到Entry的value值,然后remove它,防止内存泄露
  • 每次使用完ThreadLocal,都调用它的remove()方法,清除数据。

5.key是弱引用,GC回收会影响ThreadLocal的正常工作嘛

  • 弱引用:具有弱引用的对象拥有更短暂的生命周期。如果一个对象只有弱引用存在了,则下次GC将会回收掉该对象(不管当前内存空间足够与否)

当然不会,因为还有ThreadLocal强引用着它,是不会被GC回收的,除非手动将ThradLocal置为null

image-20230225183419928

验证

package com.jhq.threadLocal;

import java.lang.ref.WeakReference;
/**
 * @BelongsProject: study
 * @BelongsPackage: com.jhq.threadLocal
 * @Author: jianghq
 * @CreateTime: 2023-02-24  17:02
 * @Description: ThreadLocal的key既然是弱引用.会不会GC贸然把key回收掉,进而影响ThreadLocal的正常使用? 不会
 * @Version: 1.0
 */
public class WeakReferenceTest {
    public static void main(String[] args) {
        Object object = new Object();
        WeakReference<Object> testWeakReference = new WeakReference<>(object);
        System.out.println("GC回收之前,弱引用:"+testWeakReference.get());
        //触发系统垃圾回收
        for(int i=0;i<100;i++){
            //我们只能建议GC回收,并不能百分之百保证真的回收
            System.gc();
        }

        System.out.println("GC回收之后,弱引用:"+testWeakReference.get());
        //手动设置为object对象为null
        object=null;
        System.gc();
        System.out.println("对象object设置为null,GC回收之后,弱引用:"+testWeakReference.get());

    }
}
输出:
    GC回收之前,弱引用:java.lang.Object@cc34f4d
    GC回收之后,弱引用:java.lang.Object@cc34f4d
    对象object设置为nullGC回收之后,弱引用:null
    

image.png

6.Entry的key为什么要设计成弱引用呢,为什么不使用强引用

官方回答

o help deal with very large andlong-lived usages, the hash table entries use WeakReferences for keys.

为了应对非常大和长时间的用途,哈希表使用弱引用的 key。

6.1Entry的key如果使用强引用

先看看引用图

image-20230225185852595

正这种情况,由于ThreadLocalMap生命周期和Thread一样长,使用强引用之后,只要Thrad存在,那么Entry就会一直存在内存中,如果线程为核心线程池中的线程,Entry就会一直存在,导致内存泄漏。

6.2为什么要设计成弱引用

如果Key使用弱引用:ThreadLocal置为null,因为ThreadLocalMap持有ThreadLocal的弱引用,即使没有手动删除,ThreadLocal也会被回收。value则在下一次ThreadLocalMap调用set,get,remove的时候会被清除。

6.3四种引用类型

  • 强引用:我们new出来的对象就是强引用,例如Object o=new Onject();强引用不会被GC回收。
  • 软引用:一个对象只有软引用时,内存空间足够的情况下不会被回收,当内存不足时会被回收。
  • 弱引用:当对象只有弱引用时,下一次GC时会被回收。
  • 虚引用:如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。虚引用主要用来跟踪对象被垃圾回收器回收的活动。

7.我们希望父子线程之间共享数据,应该怎么做呢

7.1InheritableThreadLocal

使用InheritableThreadLocal

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

        ThreadLocal threadLocal=new ThreadLocal();
        InheritableThreadLocal inheritableThreadLocal=new InheritableThreadLocal();
        threadLocal.set("main线程.ThreadLocal");
        inheritableThreadLocal.set("main线程.InheritableThreadLocal");


        
        new Thread(()->{
            threadLocal.set(Thread.currentThread().getName()+"ThreadLocal");
            inheritableThreadLocal.set(Thread.currentThread().getName()+"InheritableThreadLocal");

        }).start();

        new Thread(()->{
            System.out.println("当前线程"+Thread.currentThread().getName()+"===="+threadLocal.get());
            System.out.println("当前线程"+Thread.currentThread().getName()+"===="+inheritableThreadLocal.get());

        }).start();

    }
}

//输出
当前线程Thread-0====null
当前线程Thread-0====main线程.InheritableThreadLocal

可以看出,ThreadLocal在父子线程之间是不共享的。InheritableThreadLocal可以在父子线程之间共享。但仅限于父子线程之间。

7.2InheritableThreadLocal是如何实现父子线程之间共享的

InheritableThreadLocal是Thread的成员变量,返回类型也是ThreadLocal.ThreadLocalMap

    /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;

    /*
     * InheritableThreadLocal values pertaining to this thread. This map is
     * maintained by the InheritableThreadLocal class.
     */
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

Thread的init方法

    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc) {
  //>.....衡略
        //如果父线程的inheritableThreadLocals不为空,则将inheritableThreadLocals赋值给子类
        if (parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
        /* Stash the specified stack size in case the VM cares */
        this.stackSize = stackSize;

        /* Set thread ID */
        tid = nextThreadID();
    }


    static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
        return new ThreadLocalMap(parentMap);
    }

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

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

相关文章

行业观察 | 内存接口芯片和CXL协议

本文对内存接口芯片的概念和CXL协议进行不完全总结。 更新&#xff1a;2022 / 02 / 25 行业观察 | 内存接口芯片背景内存接口芯片概念趋势CXL 协议背景 DRAM 内存基本被国外巨头 三星、美光 等内存模组制造商垄断&#xff0c;合计市场占有率超过 90%。 内存接口芯片 概念 什…

【Android】Android开发笔记(一)

【Android】Android开发笔记&#xff08;一&#xff09; 在Android Studio中import module和delete moduleimport moduledelete moduleAndroid Studio中App&#xff08;Module&#xff09;无法正常运行在实机上测试App一些基本概念App的工程结构结语在Android Studio中import m…

Leetcode Solutions - Part 2

1. Two Sum 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一个元素在答案里不能重复出现。 你可以按…

7-vue-1

谈谈你对MVVM的理解 为什么要有这些模式&#xff0c;目的&#xff1a;职责划分、分层&#xff08;将Model层、View层进行分类&#xff09;借鉴后端思想&#xff0c;对于前端而已&#xff0c;就是如何将数据同步到页面上 MVC模式 代表&#xff1a;Backbone underscore jquer…

【DIY Arduino基于RC气垫船】

在本教程中,我们将学习如何构建基于 Arduino 的 RC 气垫船。我将向您展示构建它的整个过程,从设计和3D打印气垫船部件(包括螺旋桨)开始,到连接电子元件和对Arduino进行编程。 您可以观看以下视频或阅读下面的书面教程。 视频地址:https://www.youtube.com/watch?v=2XIG…

二叉平衡树(C++)

背景&#xff1a; 二叉平衡树&#xff0c;就是根据二叉搜索树进行优化&#xff0c;让其速度更加的快&#xff0c;如果读者没有学过二叉搜索树&#xff0c;可以前往以下链接查看资料&#xff1a;http://t.csdn.cn/cCDQDhttp://t.csdn.cn/cCDQD 二叉搜索树的缺陷&#xff1a; 在…

【Spark分布式内存计算框架——Spark Streaming】1. Streaming 概述(上)Streaming 应用场景、Lambda 架构

前言 在很多实时数据处理的场景中&#xff0c;都需要用到流式处理&#xff08;Stream Process&#xff09;框架&#xff0c;Spark也包含了两个完整的流式处理框架Spark Streaming和Structured Streaming&#xff08;Spark 2.0出现&#xff09;&#xff0c;先阐述流式处理框架&…

Linux--TCP编程--0216 17

观前提示&#xff1a;本篇博文的一些接口需要前几篇博文实现的 线程池的实现Liunx--线程池的实现--0208 09_Gosolo&#xff01;的博客-CSDN博客 线程池的单例模式Linux--线程安全的单例模式--自旋锁--0211_Gosolo&#xff01;的博客-CSDN博客 1.TCP编程需要用的接口 创建 sock…

【数据挖掘】EDA——以2022雪浪算力开发者大赛数据为例

作者简介&#xff1a;重庆大学22级研一&#xff0c;研究方向&#xff1a;时空数据挖掘、图神经网络。目前正在学习大数据、数据挖掘等相关知识&#xff0c;希望毕业后能找到数据相关岗位。 前言 之前写了一个比赛复盘&#xff08;【竞赛复盘】2022雪浪算力开发者大赛——阀体异…

Python脚本之准备测试环境的用户数据

本文为博主原创&#xff0c;未经授权&#xff0c;严禁转载及使用。 本文链接&#xff1a;https://blog.csdn.net/zyooooxie/article/details/127645678 这期是讲述下 我准备测试环境用户数据的经历。 【实际这篇博客推迟发布N个月】 个人博客&#xff1a;https://blog.csdn.…

【读书笔记】《深入浅出数据分析》第三章 寻找最大值

目录 一&#xff0c;Excel却是最基础、最高频、最有机会展示的一款数据分析工具二&#xff0c;作为数据工作者&#xff0c;实际工作中&#xff0c;不管用不用的上&#xff0c;至少到达会的水准1&#xff0c;常用函数2&#xff0c;透视表3&#xff0c;可视化4&#xff0c;数据分…

【RabbitMQ笔记04】消息队列RabbitMQ七种模式之发布订阅模式(Publish/Subscribe)

这篇文章&#xff0c;主要介绍消息队列RabbitMQ七种模式之发布订阅模式&#xff08;Publish/Subscribe&#xff09;。 目录 一、发布订阅模式 1.1、Exchange交换机 &#xff08;1&#xff09;什么是Exchange交换机呢&#xff1f;&#xff1f;&#xff1f; &#xff08;2&am…

数据结构与算法----问答2023

1、什么是哈希表&#xff1f;如何解决碰撞&#xff1f; 哈希表&#xff08;Hash Table&#xff09;&#xff0c;也称为散列表&#xff0c;是一种用于实现字典&#xff08;键值对&#xff09;数据结构的数据结构。它将键映射到哈希表中的一个索引&#xff08;桶&#xff09;来保…

从零开始学习iftop流量监控(找出服务器耗费流量最多的ip和端口)

一、iftop是什么iftop是类似于top的实时流量监控工具。作用&#xff1a;监控网卡的实时流量&#xff08;可以指定网段&#xff09;、反向解析IP、显示端口信息等官网&#xff1a;http://www.ex-parrot.com/~pdw/iftop/二、界面说明>代表发送数据&#xff0c;< 代表接收数…

DHCP服务器的使用以及可能出现的问题(图文详细版)

DHCP服务的使用 开始&#xff0d;管理工具&#xff0d;DHCP,打开DHCP服务器选项窗口 新建作用域 在此处输入名称和描述,单击下一步 随机确定一组IP地址的范围,并指定其子网掩码 , 单击下一步 若想要排除某一个/组特定的IP地址,我们可以在此界面输入该IP地址,若没有,则可…

CTFHub | 前端验证

0x00 前言 CTFHub 专注网络安全、信息安全、白帽子技术的在线学习&#xff0c;实训平台。提供优质的赛事及学习服务&#xff0c;拥有完善的题目环境及配套 writeup &#xff0c;降低 CTF 学习入门门槛&#xff0c;快速帮助选手成长&#xff0c;跟随主流比赛潮流。 0x01 题目描述…

django项目部署(腾讯云服务器centos)

基本步骤&#xff1a; 购买腾讯云服务器并配配置好 >> 本地项目依赖收集准备 >> 上传项目等文件到服务器 >> 服务器安装部署软件和python环境 >> 开始部署&#xff08;全局来看就这5个步骤&#xff09; 目录 目录 1. 购买腾讯云服务器并配配置好 …

【算法设计技巧】分治算法

分治算法 用于设计算法的另一种常用技巧为分治算法(divide and conquer)。分治算法由两部分组成&#xff1a; 分(divide)&#xff1a;递归解决较小的问题(当然&#xff0c;基准情况除外)治(conquer)&#xff1a;然后&#xff0c;从子问题的解构建原问题的解。 传统上&#x…

升级日记本-课后程序(JAVA基础案例教程-黑马程序员编著-第七章-课后作业)

【实验7-4】 升级版日记本 【任务介绍】 1.任务描述 本案例要求编写一个模拟日记本的程序&#xff0c;通过在控制台输入指令&#xff0c;实现在本地新建日记本、打开日记本和修改日记本等功能。 用户输入指令1代表“新建日记本”&#xff0c;可以从控制台获取用户输入的日记…

物联网MQTT协议简单介绍

物联网曾被认为是继计算机、互联网之后&#xff0c;信息技术行业的第三次浪潮。随着基础通讯设施的不断完善&#xff0c;尤其是 5G 的出现&#xff0c;进一步降低了万物互联的门槛和成本。物联网本身也是 AI 和区块链应用很好的落地场景之一&#xff0c;各大云服务商也在纷纷上…