【JUC】线程局部变量ThreadLocal

news2024/11/19 14:36:27

文章目录

  • ThreadLocal简介
    • 面试题
    • 是什么?
    • 能干吗?
    • API介绍
      • initialValue方法(不推荐)
      • withInitial方法(推荐)
      • remove
    • ThreadLocal入门案例
      • 原始需求
      • 需求变更
      • 线程池
    • 总结
  • ThreadLocal源码分析
    • Thread、ThreadLocal、ThreadLocalMap关系
      • Thread和ThreadLocal:每个线程有一份ThreadLocalMap
      • ThreadLocal和ThreadLocalMap
      • 类关系概括
    • 总结
  • ThreadLocal内存泄漏问题
    • 什么是内存泄漏
    • 谁惹的祸?
      • 回首ThreadLocalMap
      • finalize方法
      • 强软弱虚引用
        • 强引用
        • 软引用
          • 使用场景
        • 弱引用
        • 虚引用
    • 为什么要用弱引用?不用如何?
      • 为什么要用弱引用
      • 弱引用需要注意的问题:
      • 清除脏Entry(调用expungestaleEntry方法)
      • set()方法
      • get()方法
      • remove()
  • 总结
    • 最佳实践
  • 文章说明

ThreadLocal简介

面试题

  • ThreadLocal中ThreadLocalMap的数据结构和关系是什么?
  • ThreadLocal的key是弱引用,这是为什么?
  • ThreadLocal内存泄漏问题你知道吗?
  • ThreadLocal中最后为什么要加remove方法?

是什么?

ThreadLocal提供线程局部变量。这些变量与正常的变量不同,因为每一个线程在访问ThreadLocal实例的时候(通过其get或set方法)都有自己的、独立初始化的变量副本。ThreadLocal实例通常是类中的私有静态字段,使用它的目的是希望将状态(例如用户ID或事物ID)与线程关联起来。

能干吗?

实现每一个线程都有自己专属的本地变量副本(自己用自己的变量不用麻烦别人,不和其他人共享,人人有份,人各一份)(如和平精英里面,每个线程的血量、装备都不同)。主要解决了让每个线程绑定自己的值,通过使用get()和set()方法,获取默认值或将其改为当前线程所存的副本的值从而避免了线程安全问题,不用加锁。比如8锁案例中,资源类是使用同一部手机,多个线程抢夺同一部手机,假如一人一台就不用抢了

在这里插入图片描述

API介绍

在这里插入图片描述

initialValue方法(不推荐)

protected T initialValue(): 返回此线程局部变量的当前线程的“初始值”。此方法将在一个线程首次访问此变量并通过 get() 方法获取其值时被调用,除非该线程之前已通过 set(T) 方法设置了该值,在这种情况下,initialValue 方法将不会被该线程调用。通常,每个线程只会调用此方法一次,但如果在调用 remove() 方法之后紧接着调用 get() 方法,则可能会再次调用此方法。此方法的基本实现仅返回 null;如果程序员希望线程局部变量具有除 null 之外的初始值,必须子类化 ThreadLocal 并重写此方法。通常使用匿名内部类来实现。返回的结果是此线程局部变量的初始值。

ThreadLocal<Integer> saleVolume = new ThreadLocal<Integer>() {
    @Override
    protected Integer initialValue() {
        return 0;
    }
};

不推荐使用,因为匿名内部类写起来比较麻烦

withInitial方法(推荐)

作用和上面的相同

ThreadLocal<Integer> saleVolume = ThreadLocal.withInitial(() -> 0);

remove

在这里插入图片描述

不用线程池的话,会被垃圾回收,不remove也不会内存溢出

ThreadLocal入门案例

原始需求

5个销售卖房子,集团只关心销售总量的准确统计数,按照总销售额统计,方便集团公司给员工发送奖金(群雄逐鹿起纷争,为了数据安全只能加锁)

/**
需求:5个销售卖房子,集团只关心销售总量的精确统计数
*/
class House {
    int saleCount = 0;
    public synchronized void saleHouse() {
        ++saleCount;
    }
}
public class ThreadLocalDemo {
    public static void main(String[] args) {
        House house = new House();
        for (int i = 1; i <= 5; i++) {
            new Thread(() -> {
                int size = new Random().nextInt(5) + 1;
                System.out.println(size);
                for (int j = 1; j <= size; j++) {
                    house.saleHouse();
                }
            }, String.valueOf(i)).start();
        }
        try {
            TimeUnit.MILLISECONDS.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "\t" + "共计卖出多少套: " + house.saleCount);
    }
}

在这里插入图片描述

需求变更

希望各凭销售本事提成,按照出单数各自统计(比如房产中介销售都有自己的销售额)

class House {
    int saleCount = 0;
    public synchronized void saleHouse() {
        saleCount++;
    }
    // 定义一个线程局部变量,初始值是0
    ThreadLocal<Integer> saleVolume = ThreadLocal.withInitial(() -> 0);
    public void saleVolumeByThreadLocal() {
        saleVolume.set(1 + saleVolume.get());
    }
}

public class ThreadLocalDemo {
    public static void main(String[] args) {
        House house = new House();
        for (int i = 1; i <= 5; i++) {
            new Thread(() -> {
                int size = new Random().nextInt(5) + 1;
                try {
                    for (int j = 1; j <= size; j++) {
                        // 统计总数
                        house.saleHouse();
                        // 更新线程的数量(每个销售自己的销量)
                        house.saleVolumeByThreadLocal();
                    }
                    System.out.println(Thread.currentThread().getName() + "\t" + "号销售卖出:" + house.saleVolume.get());
                } finally {
                    // 记得remove
                    house.saleVolume.remove();
                }
            }, String.valueOf(i)).start();
        }
        try {
            TimeUnit.MILLISECONDS.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "\t" + "共计卖出多少套: " + house.saleCount);
    }
}

在这里插入图片描述

线程池

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

class MyData {
    ThreadLocal<Integer> threadLocalField = ThreadLocal.withInitial(() -> 0);

    public void add() {
        threadLocalField.set(1 + threadLocalField.get());
    }
}

/**
 * .【强制】必须回收自定义的 ThreadLocal 变量,尤其在线程池场景下,线程经常会被复用,如果不清理
 * 自定义的 ThreadLocal 变量,可能会影响后续业务逻辑和造成内存泄露等问题。尽量在代理中使用
 * try-finally 块进行回收。
 */
public class ThreadLocalDemo2 {
    public static void main(String[] args) throws InterruptedException {
        MyData myData = new MyData();

        ExecutorService threadPool = Executors.newFixedThreadPool(3);

        try {
            for (int i = 0; i < 10; i++) {
                threadPool.submit(() -> {
                    Integer beforeInt = myData.threadLocalField.get();
                    myData.add();
                    Integer afterInt = myData.threadLocalField.get();
                    System.out.println(Thread.currentThread().getName() + "\t" + "beforeInt:" + beforeInt + "\t afterInt: " + afterInt);
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPool.shutdown();
        }

    }
}

在这里插入图片描述

添加remove

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

class MyData {
    ThreadLocal<Integer> threadLocalField = ThreadLocal.withInitial(() -> 0);

    public void add() {
        threadLocalField.set(1 + threadLocalField.get());
    }
}

/**
 * .【强制】必须回收自定义的 ThreadLocal 变量,尤其在线程池场景下,线程经常会被复用,如果不清理
 * 自定义的 ThreadLocal 变量,可能会影响后续业务逻辑和造成内存泄露等问题。尽量在代理中使用
 * try-finally 块进行回收。
 */
public class ThreadLocalDemo2 {
    public static void main(String[] args) throws InterruptedException {
        MyData myData = new MyData();

        ExecutorService threadPool = Executors.newFixedThreadPool(3);

        try {
            for (int i = 0; i < 10; i++) {
                threadPool.submit(() -> {
                    try {
                        Integer beforeInt = myData.threadLocalField.get();
                        myData.add();
                        Integer afterInt = myData.threadLocalField.get();
                        System.out.println(Thread.currentThread().getName() + "\t" + "beforeInt:" + beforeInt + "\t afterInt: " + afterInt);
                    } finally {
                        myData.threadLocalField.remove();
                    }
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPool.shutdown();
        }

    }
}

在这里插入图片描述

总结

  • 每个Thread内有自己的实例副本且该副本只有当前线程自己使用
  • 其他ThreadLocal不可访问,不存在多线程间共享问题
  • 统一设置初始值,但是每个线程对这个值得修改都是各自线程互相独立的
  • 如何才能不争抢
    • 加入synchronized或者Lock控制资源的访问顺序
    • ThreadLocal:人手一份,不用争抢

ThreadLocal源码分析

Thread、ThreadLocal、ThreadLocalMap关系

Thread和ThreadLocal:每个线程有一份ThreadLocalMap

在这里插入图片描述

ThreadLocal和ThreadLocalMap

在这里插入图片描述

类关系概括

  • ThreadLocalMap实际上就是一个以ThreadLocal实例为Key,任意对象为value的Entry对象
  • 当我们为ThreadLocal变量赋值,实际上就是以当前ThreadLocal实例为Key,值为value的Entry往这个ThreadLocalMap中存放

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

ThreadLocal是一个壳子,它只是自己作为一个key来让线程从ThreadLocalMap获取value,真正的存储结构是ThreadLocal里有ThreadLocalMap这么个内部类,每个Thread对象维护着一个ThreadLocalMap的引用,ThreadLocalMap是ThreadLocal的内部类,用Entry来进行存储。

1、调用ThreadLocal的set()方法时,实际上就是往ThreadLocalMap设置值,key是ThreadLocal对象,值Value是传递进来的对象

2、调用ThreadLocal的get()方法时,实际上就是往ThreadLocallap获取值,key是ThreadLocal对象

正因为这个原理,所以ThreadLocal能够实现“数据隔离”,获取当前线程的局部变量值,不受其他线程影响~

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

总结

  • ThreadLocalMap从字面上就可以看出这是一个保存ThreadLocal对象的map(其实是以ThreadLocal为Key),不过是经过了两层包装的ThreadLocal对象

在这里插入图片描述

  • JVM内部维护了一个线程版的Map<ThreadLocal, Value>(通过ThreadLocal对象的set方法,把ThreadLocal对象自己当作Key,放进了ThreadLocalMap中),每个线程要用到这个T的时候,用当前的线程去Map里面获取,通过这样让每个线程都拥有了自己独立的变量,人手一份,竞争条件被彻底消除,在并发模式下绝对安全

ThreadLocal内存泄漏问题

什么是内存泄漏

不会被使用的对象或者变量占用的内存不能被回收,就是内存泄漏

谁惹的祸?

回首ThreadLocalMap

ThreadLocalMap从字面上就可以看出这是一个保存ThreadLocal对象的map(以ThreadLocal为Key),不过是经过了两层包装的ThreadLocal对象:

(1)第一层包装是使用 WeakReference<ThreadLocal<>>将ThreadLocal对象变成一个弱引用的对象

(2)第二层包装是定义了一个专门的类 Entry来扩展 WeakReference<ThreadLocal<?>>

在这里插入图片描述

finalize方法

  • 当垃圾回收器确定没有对该对象的更多引用时,由对象上的垃圾回收器调用。子类重写finalize方法以处置系统资源或执行其他清理
  • finalize的通常目的是在对象被不可撤销地丢弃之前,执行清理操作

强软弱虚引用

在这里插入图片描述

在这里插入图片描述

强引用
  • 强引用是我们最常见的普通对象引用,只要还有强引用指向一个对象,就能表明对象还“活着”,垃圾回收器不会碰这种对象
  • 在Java 中最常见的就是强引用,把一个对象赋给一个引用变量(Student s1 = new Student()),这个引用变量就是一个强引用
  • 当一个对象被强引用变量引用时,它处于可达状态,它是不可能被垃圾回收机制回收的
  • 对于强引用的对象,就算是出现了OOM也不会对该对象进行回收,即使该对象以后永远都不会被用到,JVM也不会回收,因此强引用是造成Java内存泄露的主要原因之一
  • 对于一个普通的对象,如果没有其他的引用关系,只要超过了引用的作用域或者显式地将相应(强)引用赋值为null,就可以被垃圾回收的了(当然具体回收时机还是要看垃圾回收策略)
class MyObject {
    //这个方法一般不用复写,我们只是为了教学给大家演示案例做说明
    @Override
    protected void finalize() throws Throwable {
        // finalize的通常目的是在对象被不可撤销地丢弃之前执行清理操作。
        System.out.println("-------invoke finalize method~!!!");
    }
}

private static void strongReference() {
    MyObject myObject = new MyObject();
    System.out.println("gc before: " + myObject);

    myObject = null;
    System.gc();//人工开启GC,一般不用

    //暂停毫秒
    try {
        TimeUnit.MILLISECONDS.sleep(500);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    System.out.println("gc after: " + myObject);
}

在这里插入图片描述

软引用
  • 是一种比强引用弱一些的引用,需要用java.lang.ref.SoftReference类来实现,可以让对象豁免一些垃圾回收
  • 对于只有软引用的对象而言,当系统内存充足时,不会被回收;当系统内存不足时,会被回收
  • 软引用通常用在对内存敏感的程序中,比如高速缓存,内存够用就保留,不够用就回收

在这里插入图片描述

private static void softReference() {
    SoftReference<MyObject> softReference = new SoftReference<>(new MyObject());
    //System.out.println("-----softReference:"+softReference.get());

    System.gc();
    try {
        TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("-----gc after内存够用: " + softReference.get());

    try {
        byte[] bytes = new byte[20 * 1024 * 1024];//20MB对象
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        System.out.println("-----gc after内存不够: " + softReference.get());
    }
}

在这里插入图片描述

使用场景

假如有一个应用需要读取大量的本地图片

  • 如果每次读取图片都从硬盘读取则会严重影响性能
  • 如果一次性全部加载到内存中又可能会造成内存溢出
  • 此时使用软应用来解决,设计思路时:用一个HashMap来保存图片的路径和与相应图片对象关联的软引用之间的映射关系,在内存不足时,JVM会自动回收这些缓存图片对象所占用的空间,有效避免了OOM的问题
Map<String, SoftReference<Bitmap>> imageCache = new HashMap<String, SoftReference<Bitmap>>();
弱引用
  • 弱引用需要用java.lang.ref.WeakReference类来实现,它比软引用的生存期更短
  • 对于只有弱引用的对象而言,只要垃圾回收机制一运行,不管JVM的内存空间是否足够,都会回收该对象占用的内存。
private static void weakReference() {
    WeakReference<MyObject> weakReference = new WeakReference<>(new MyObject());
    System.out.println("-----gc before 内存够用: " + weakReference.get());

    System.gc();
    //暂停几秒钟线程
    try {
        TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    System.out.println("-----gc after 内存够用: " + weakReference.get());
}

在这里插入图片描述

虚引用
  • 虚引用必须和引用队列ReferenceQueue联合使用,需要调用java.lang.ref.PhantomReference类来实现,如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都有可能被垃圾回收器回收,它不能单独使用也不能通过它访问对象。
  • PhantomReference的get方法总是返回null,虚引用的主要作用是跟踪对象被垃圾回收的状态,不是业务逻辑处理的方法。仅仅是提供了一种确保对象被finalize后,做某些事情的通知机制
  • 设置虚引用关联对象的唯一目的就是在对象被GC的时候会收到一个系统通知或者后续添加进一步的处理,用来实现比finalize机制更灵活的回收操作
  • 虚引用的对象,在回收前,会进入引用队列,后续可以弹出队列的元素,看看弹出的时候要做一些什么操作
public static void main(String[] args) {
    MyObject myObject = new MyObject();
    // 引用队列
    ReferenceQueue<MyObject> referenceQueue = new ReferenceQueue<>();
    PhantomReference<MyObject> phantomReference = new PhantomReference<>(myObject, referenceQueue);
    //System.out.println(phantomReference.get());

    List<byte[]> list = new ArrayList<>();

    new Thread(() -> {
        while (true) {
            list.add(new byte[1 * 1024 * 1024]);
            try {
                TimeUnit.MILLISECONDS.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(phantomReference.get() + "\t" + "list add ok");
        }
    }, "t1").start();

    new Thread(() -> {
        while (true) {
            Reference<? extends MyObject> reference = referenceQueue.poll();
            if (reference != null) {
                System.out.println("-----有虚对象回收加入了队列");
                break;
            }
        }
    }, "t2").start();

}

设置堆内存为10M,运行

在这里插入图片描述

为什么要用弱引用?不用如何?

在这里插入图片描述

为什么要用弱引用

  • 当function1方法执行完毕后,栈帧销毁,方法的局部变量出栈了,强引用t1也就没有了,但此时线程的ThreadLocalMap(线程还存活,ThreadLocalMap就存活)里某个entry的Key引用还指向这个对象,若这个Key是强引用,就会导致Key指向的ThreadLocal对象即V指向的对象不能被gc回收,造成内存泄露(就像人去世了,身份证要注销)
  • 若这个引用时弱引用就大概率会减少内存泄漏的问题(当然,还得考虑key为null这个坑),使用弱引用就可以使ThreadLocal对象在方法执行完毕后顺利被回收且entry的key引用指向为null
  • 我们调用get,set或remove方法时,就会尝试删除key为null的entry,可以释放value对象所占用的内存

弱引用需要注意的问题:

  • 当我们为threadLocal变量赋值,实际上就往这个threadLocalMap中存放(threadLocal实例为key,值为value)。Entry中的key是弱引用,当threadLocal外部强引用被置为null(t1 = null),那么系统GC的时候,根据可达性分析,这个threadLocal实例就没有任何一条链路能够引用到它,这个ThreadLocal势必会被回收,但是value还没有回收。这样一来,ThreadLocalMap中就会出现key为null的Entry,没有办法访问这些key为null的Entry的value。如果当前线程迟迟不结束的话(线程池场景),这些key为null的Entry的value就会一直存在一条强引用链:Thread Ref-> Thread-> ThreaLocalMap-> Entry ->value,这样value永远无法回收,造成内存泄漏
    • 当前如果当前thread运行结束,threadLocal,threadLocalMap,Entry没有引用链可达,在垃圾回收的时候都会被系统进行回收
    • 但在实际使用中我们有时候会用线程池去维护我们的线程,比如在Executors.newFixedThreadPool()时创建线程的时候,为了复用线程是不会结束的,所以threadLocal内存泄漏就值得我们小心
  • 虽然弱引用保证了Key指向的ThreadLocal对象能够被及时回收,但是v指向的value对象是需要ThreadLocalMap调用get、set时发现key为null时才会去回收整个entry、value,因此弱引用不能100%保证内存不泄露,我们要在不使用某个ThreadLocal对象后,手动调用remove方法来删除它,尤其是在线程池中,不仅仅是内存泄漏的问题,因为线程池中的线程是重复使用的,意味着这个线程的ThreadLocalMap对象也是重复使用的,如果我们不手动调用remove方法,那么后面的线程就有可能获取到上个线程遗留下来的value值,造成bug。

清除脏Entry(调用expungestaleEntry方法)

脏Entry即key为null的entry

set()方法

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

get()方法

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

remove()

在这里插入图片描述

在这里插入图片描述

总结

  • ThreadLocal并不解决线程间共享数据的问题
  • ThreadLocal适用于变量在线程间隔离且在方法间共享的场景
  • ThreadLocal通过隐式的在不同线程内创建独立实例副本避免了实例线程安全的问题
  • 每个线程持有一个只属于它自己的专属map并维护了ThreadLocal对象与具体实例的映射,该Map由于只被持有他的线程访问,故不存在线程安全以及锁的问题
  • ThreadLocalMap的Entry对ThreadLocal的引用为弱引用。避免了ThreadLocal对象无法被回收的问题
  • 都会通过expungeStaleEntrycleanSomeSlotsreplaceStaleEntry这三个方法回收键为null的Entry对象的值(即为具体实例)以及entry对象本身从而防止内存泄漏,属于安全加固的方法

最佳实践

  • ThreadLocal一定要初始化ThreadLocal.withInitiaL(()->初始化值),避免空指针异常
  • 建议把ThreadLocal修饰为static(建议),ThreadLocal可以只初始化一次,只分配一块存储空间就可以了,没必要作为成员变量多次被初始化
  • 用完记得手动remove(强制)

文章说明

该文章是本人学习 尚硅谷 的学习笔记,文章中大部分内容来源于 尚硅谷 的视频尚硅谷JUC并发编程(对标阿里P6-P7),也有部分内容来自于自己的思考,发布文章是想帮助其他学习的人更方便地整理自己的笔记或者直接通过文章学习相关知识,如有侵权请联系删除,最后对 尚硅谷 的优质课程表示感谢。

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

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

相关文章

区块链——hardhat使用

一、引入hardhat yarn add --dev hardhat // 引入验证合约的插件 yarn add --dev nomicfoundation/hardhat-verify 二、创建hardhat项目 yarn hardhat 三、编写我们的合约 四、编译我们的合约 yarn hardhat compile 五、编写脚本部署合约以及验证合约 // 获取hardhat环境对象 c…

Flutter大型项目架构:私有组件包管理

随着项目功能模块越来越多&#xff0c;怎么去管理这些私有组件包是一个不得不面对的问题&#xff0c;特别对于团队开发来讲&#xff0c;一些通用的公共组件往往会在多个项目间使用&#xff0c;多的有几十个&#xff0c;每个组件包都有有自己的版本&#xff0c;组件包之间还有依…

AI的欺骗游戏:揭示多模态大型语言模型的易受骗性

人工智能咨询培训老师叶梓 转载标明出处 多模态大型语言模型&#xff08;MLLMs&#xff09;在处理包含欺骗性信息的提示时容易生成幻觉式响应。尤其是在生成长响应时&#xff0c;仍然是一个未被充分研究的问题。来自 Apple 公司的研究团队提出了MAD-Bench&#xff0c;一个包含8…

漏洞复现-F6-11泛微-E-Cology-SQL

本文来自无问社区&#xff0c;更多漏洞信息可前往查看http://www.wwlib.cn/index.php/artread/artid/15575.html 0x01 产品简介 泛微协同管理应用平台e-cology是一套企业级大型协同管理平台 0x02 漏洞概述 该漏洞是由于泛微e-cology未对用户的输入进行有效的过滤&#xff0…

CentOS_7.9历史版本官网下载

文章目录 1. 官网下载1.1. 打开官网1.2. download1.3. Older Versions-click here1.4. RPMs1.5. Vault mirror1.6. 7.9.2009/1.7. isos1.8. x86_641.9. 选择安装包下载 2. 阿里云镜像站下载 1. 官网下载 下载地址(如果下载地址已失效&#xff0c;参考下面的步骤) iso是安装包…

【前端】一文带你了解 CSS

文章目录 1. CSS 是什么2. CSS 引入方式2.1 内部样式2.2 外部样式2.3 内联样式 3. CSS 常见选择器3.1 基础选择器3.1.1 标签选择器3.1.2 类选择器3.1.3 id 选择器3.1.4 通配符选择器 3.2 复合选择器3.2.1 后代选择器 4. CSS 常用属性4.1 字体相关4.2 文本相关4.3 背景相关4.4 设…

基于sklearn的机器学习 — 决策树与随机森林

基于树的学习算法是一种广泛而流行的非参数、有监督的分类和回归方法。 基于树的学习算法的基础是决策树&#xff08;decision tree&#xff09;&#xff0c;它将一系列决策规则串联起来&#xff0c;看起来像一棵倒立的树&#xff0c;第一条决策规则位于树顶&#xff0c;称之为…

第十四篇——军争篇:怎样在行军中设计战场

目录 一、背景介绍二、思路&方案三、过程1.思维导图2.文章中经典的句子理解3.学习之后对于投资市场的理解4.通过这篇文章结合我知道的东西我能想到什么&#xff1f; 四、总结五、升华 一、背景介绍 通过不利的战场&#xff0c;用方式方法&#xff0c;让战场逐渐转化成对自…

[JS]同事:这次就算了,下班回去赶紧补补内置函数,再犯肯定被主管骂

【版权声明】未经博主同意&#xff0c;谢绝转载&#xff01;&#xff08;请尊重原创&#xff0c;博主保留追究权&#xff09; https://blog.csdn.net/m0_69908381/article/details/140754278 出自【进步*于辰的博客】 参考笔记一&#xff0c;P10.4、P13.2&#xff1b;笔记三&am…

城市交通工具目标检测数据集自行车、公交车、小汽车、行人

数据整理不易&#xff0c;下载地址点这里&#xff1b; yolo格式数据集之交通工具检测5种&#xff1b; 数据集已划分好|可以直接使用|yolov5|v6|v7|v8|v9|v10通用&#xff1b; 本数据为交通工具检测检测数据集&#xff0c;数据集数量如下&#xff1a; 总共有:6633张 训练集&…

尚庭公寓(四)

房间基本属性管理共有五个接口&#xff0c;分别是**保存或更新属性名称**、**保存或更新属性值**、**查询全部属性名称和属性值列表**、**根据ID删除属性名称**、**根据ID删除属性值**。下面逐一是实现。 首先在AttrController中注入AttrKeyService和AttrValueService&#xf…

计数器与阻塞队列

目录 一&#xff1a;阻塞队列 模拟阻塞队列 二&#xff1a;线程池&#xff1a; 三&#xff1a;计数器&#xff1a; 定时器模拟实现 一&#xff1a;阻塞队列 阻塞队列是在原有的普通队列上做了扩充&#xff0c;标准库中原有的队列和子类都是线程不安全的。 1.线程安全 2.…

OSMDroidOfflineDemo源码调试记录

文章目录 源码下载环境配置尝试不同离线加载遇到的问题 尝试安卓端加载离线地图&#xff0c;下载了使用osmdroid的离线版项目源码&#xff0c;更改JDK环境、gradle环境&#xff0c;一顿操作下来&#xff0c;踉踉跄跄的把程序跑起来了&#xff0c;但是离线的地图一直加载不出来。…

49.TFT_LCD液晶屏驱动设计与验证(2)

&#xff08;1&#xff09;Visio视图&#xff1a; &#xff08;2&#xff09;控制模块Verilog代码&#xff1a; module tft_ctrl(input clk_33M ,input reset_n ,input [23:0] data_in ,output [9:0] hang…

如何使用 SQLite ?

SQLite 是一个轻量级、嵌入式的关系型数据库管理系统&#xff08;RDBMS&#xff09;。它是一种 C 库&#xff0c;实现了自给自足、无服务器、零配置、事务性 SQL 数据库引擎。SQLite 的源代码是开放的&#xff0c;完全在公共领域。它被广泛用于各种应用程序&#xff0c;包括浏览…

Python | ValueError: could not convert string to float: ‘example’

Python | ValueError: could not convert string to float: ‘example’ 在Python编程中&#xff0c;类型转换是一个常见的操作。然而&#xff0c;当尝试将一个字符串转换为浮点数时&#xff0c;如果字符串的内容不是有效的浮点数表示&#xff0c;就会遇到“ValueError: could…

【python_将一个列表中的几个字典改成二维列表,并删除不需要的列】

def 将一个列表中的几个字典改成二维列表(original_list,headersToRemove_list):# 初始化一个列表用于存储遇到的键&#xff0c;保持顺序ordered_keys []# 遍历data中的每个字典&#xff0c;添加其键到ordered_keys&#xff0c;如果该键还未被添加for d in original_list:for …

SpringCloud之@FeignClient()注解的使用方式

FeignClient介绍 FeignClient 是 Spring Cloud 中用于声明一个 Feign 客户端的注解。由于SpringCloud采用分布式微服务架构&#xff0c;难免在各个子模块下存在模块方法互相调用的情况。比如订单服务要调用库存服务的方法&#xff0c;FeignClient()注解就是为了解决这个问题的…

Vim 文本编辑工具

Vim 基础命令 一、Vim 命令速查 Vim 是一款功能强大的文本编辑器&#xff0c;广泛应用于Linux系统中。以下是一些基础但非常有用的Vim命令&#xff0c;它们将帮助你更高效地使用Vim。 使用单个字母键通常需要进一步的输入以形成完整命令。特殊符号用来表示操作的位置。 命令…

Linux网络:传输层TCP协议(四)拥塞控制及延迟应答

目录 一、拥塞控制 二、延迟应答 一、拥塞控制 虽然 TCP 拥有滑动窗口这个大杀器机制来根据具体情况对发送的数据大小和速度进行实时控制, 能够高效并且可靠的发送大量的数据. 但是如果在双方建立好连接后的刚开始阶段就发送大量的数据。仍然可能引发一些问题. 因为同一个网…