java--ThreadLocal详解

news2024/9/20 14:17:35

目录

1.ThreadLocal的用途两个使用场景

1.1场景1

1.2两个线程分别用自己的SimpleDateFormat

1.3使用线程池创建1000个打印线程分别用自己的SimpleDateFormat

 打印可以看到有很多处两个重复的日期

1.4加锁解决线程安全问题

1.5SimpleDateFormat小结

1.5更好的解决方案是使用ThreadLoacl

1.6总结

1.2场景2

 1.2.1方法

 1.3总结

1.3.1两个作用

1.3.2根据共享对象的生成时机不同,选择initialValue或者set来保存对象

1.3.3带来的好处

2.ThreadLocal原理

2.1主要方法

1.T initialValue()

2.set(T value) 为这个线程设一个新值

3.get() 得到这个线程对应的value 

 4.remove() 删除对应这个线程的值

2.2ThreadLocalMap

3.ThreadLocal使用注意点

3.1内存泄露

3.2Key的泄露

3.3如何避免内存泄露

 3.2ThreadLocal带来的空指针异常

3.3共享对象

3.4注意点

3.5实际应用场景

DateTimeContextHolder


 

ThreadLocal是本地线程,不是公用的线程。

1.ThreadLocal的用途两个使用场景

1.1场景1

每个线程对象需要一个共享对象(通常是工具类,典型需要使用的类有SimpleDateFormat和Radmon)

每个Thread内有自己在实例副本,不共享。

举个例子:教材只有一本,30个同学都抢着看。一起做笔记有线程安全问题,并发的读写会带来数据不一致,用了ThreadLocal后相当于把这本教材复印了30份教材,每个同学都使用自己的教材,这里的每一本书每一个实例,都只能当前同学当前的线程可以访问到,并且使用。

1.2两个线程分别用自己的SimpleDateFormat

package cn.butool;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 用两个线程分别打印日期信息
 */
public class ThreadLocalSimpleDateFormatDemo {
    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            System.out.println(new ThreadLocalSimpleDateFormatDemo().dataToString(10));
        }
        );
        thread1.start();

        Thread thread2 = new Thread(() -> {
            System.out.println(new ThreadLocalSimpleDateFormatDemo().dataToString(212104277));
        }
        );
        thread2.start();
    }

    /**
     * 将秒转换成日期字符串
     *
     * @param seconds
     * @return
     */
    public String dataToString(int seconds) {
        //参数的单位是毫秒 是从1970.1.1 00:00:00 GMT计时
        Date date = new Date(seconds * 1000);
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        return simpleDateFormat.format(date);
    }
}

 并没有问题

1970-01-20 10:34:39
1970-01-01 08:00:10

1.3使用线程池创建1000个打印线程分别用自己的SimpleDateFormat

package cn.butool;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 用线程池创建分别打印日期信息
 */
public class ThreadLocalSimpleDateFormatDemo {
    private static ExecutorService executorService = Executors.newFixedThreadPool(10);
    //dateFormat 不需要每次都新建
    private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
    public static void main(String[] args) throws InterruptedException {

        for (int i = 0; i < 1000; i++) {
            int finalI = i;
            executorService.submit(() -> {
                System.out.println(new ThreadLocalSimpleDateFormatDemo().dataToString(10+ finalI));
            });
        }
        executorService.shutdown();

    }

    /**
     * 将秒转换成日期字符串
     *
     * @param seconds
     * @return
     */
    public String dataToString(int seconds) {
        //参数的单位是毫秒 是从1970.1.1 00:00:00 GMT计时
        Date date = new Date(seconds * 1000);
        return simpleDateFormat.format(date);
    }
}

 打印可以看到有很多处两个重复的日期

1970-01-01 08:15:51
1970-01-01 08:16:14
1970-01-01 08:16:13
1970-01-01 08:16:12
1970-01-01 08:16:17
1970-01-01 08:16:18
1970-01-01 08:16:11
1970-01-01 08:16:11

1.4加锁解决线程安全问题

package cn.butool.threadlocal;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 用线程池创建分别打印日期信息
 */
public class ThreadLocalSimpleDateFormatDemo {
    private static ExecutorService executorService = Executors.newFixedThreadPool(10);
    //dateFormat 不需要每次都新建
    private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
    public static void main(String[] args) throws InterruptedException {

        for (int i = 0; i < 1000; i++) {
            int finalI = i;
            executorService.submit(() -> {
                System.out.println(new ThreadLocalSimpleDateFormatDemo().dataToString(10+ finalI));
            });
        }
        executorService.shutdown();

    }

    /**
     * 将秒转换成日期字符串
     *
     * @param seconds
     * @return
     */
    public String dataToString(int seconds) {
        //参数的单位是毫秒 是从1970.1.1 00:00:00 GMT计时
        Date date = new Date(seconds * 1000);
        String format=null;
        synchronized (ThreadLocalSimpleDateFormatDemo.class){
            format = simpleDateFormat.format(date);
        }
        return format;
    }
}

1.5SimpleDateFormat小结

  1. 两个线程分别用自己的SimpleDateFormat
  2. 1000个线程要用到线程池了,否则消耗内存太多
  3. 优化代码,所有线程共用一个SimpleDateFormat对象
  4. 加锁解决重复对象,但是降低效率

1.5更好的解决方案是使用ThreadLoacl

利用ThreadLocal给每个线程分配自己的DateFormat对象,同时保证了线程安全。高效利用了内存

package cn.butool.threadlocal;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 利用ThreadLocal给每个线程分配自己的DateFormat对象,
 * 同时保证了线程安全。高效利用了内存
 */
public class ThreadDateFormatResult {
    private static ExecutorService executorService = Executors.newFixedThreadPool(10);
    public static void main(String[] args) throws InterruptedException {

        for (int i = 0; i < 1000; i++) {
            int finalI = i;
            executorService.submit(() -> {
                System.out.println(new ThreadDateFormatResult().dataToString(10+ finalI));
            });
        }
        executorService.shutdown();

    }

    /**
     * 将秒转换成日期字符串
     *
     * @param seconds
     * @return
     */
    public String dataToString(int seconds) {
        //参数的单位是毫秒 是从1970.1.1 00:00:00 GMT计时
        Date date = new Date(seconds * 1000);
        SimpleDateFormat simpleDateFormat = ThreadSafeDateFormat.dateFormatThreadLocalLambda.get();
        return simpleDateFormat.format(date);
    }

     static class ThreadSafeDateFormat{
        public static ThreadLocal<SimpleDateFormat> dateFormatThreadLocal = new ThreadLocal<SimpleDateFormat>(){
            @Override
            protected SimpleDateFormat initialValue() {
                return new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
            }
        };

         /**
          * Lambda表达式写法和dateFormatThreadLocal效果相同
          */
        public static ThreadLocal<SimpleDateFormat> dateFormatThreadLocalLambda = ThreadLocal.withInitial(()->new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"));
    }
}

1.6总结

  1. 两个线程分别用自己的SimpleDateFormat没有问题
  2. 延深1000个需要使用线程池,否则消耗内存过大
  3. 使用线程池后发现,最好使用同一个SimpleDateFormat对象,否则创建1000个任务也是创建1000对象
  4. 发现是线程不安全的,出现并发安全问题
  5. 选择加锁,枷锁结果正常,但是效率低
  6. 使用更好的解决方案ThreadLocal,线程安全的,每个线程内都有一个自己的独享的SimpleDateFormat对象

1.2场景2

每个线程内需要保存,全局变量(在拦截器中获取用户信息)可以让不同方法直接使用,避免参数传递的麻烦

service调用,每个都需要user对象,层层传递,代码冗余且不易维护。

每个线程保存全局变量,可以让不同的方法直接使用。避免参数传递的麻烦。

 1.2.1方法

  1. 用ThreadLocal保存一些业务内容(用户权限信息、从用户系统获取到的用户名,userId等)
  2. 这些信息在同一个线程内相同,但不同的线程使用的业务内容是不同的
  3. 使用ThreadLocal,可以在不影响性能的情况下,无需层层传递,就可以保存当前线程内的用户信息
  4. 强调是同一个请求内,同一个线程内,不同方法间的共享
  5. 不需要重写initialValue方法
package cn.butool.threadlocal;

/**
 * 演示threadLocal避免传递参数的代码冗余
 */
public class UseThreadLocalSaveUser {
    public static void main(String[] args) {
        new Service1().process();

    }
}

class Service1 {
    // 读取到用户信息
    public void process() {
        User user = new User("busl");
        UserContextHolder.holder.set(user);
        new Service2().process();
    }
}

class Service2 {
    // 读取到用户信息
    public void process() {
        System.out.println("service2"+UserContextHolder.holder.get().getName());
        new Service3().process();
    }
}
class Service3 {
    // 读取到用户信息
    public void process() {
        System.out.println("service3"+UserContextHolder.holder.get().getName());
    }
}

/**
 * 用户上下文持有者
 */
class UserContextHolder{
    public static ThreadLocal<User> holder = new ThreadLocal<>();
}
class User {
    private String name;

    public User(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
service2busl
service3busl

 1.3总结

1.3.1两个作用

  1. 让某个需要用的对象在线程间隔离(每个线程都有自己独立的对象)
  2. 在任何方法中轻松的获取到对象

1.3.2根据共享对象的生成时机不同,选择initialValue或者set来保存对象

  1. 在ThreadLocal第一次get的时候把对象给初始化出来,对象的初始化时机可以由我们控制
  2. 如果需要保存到ThreadLocal里的对象的生成时机不由我们随意控制,例如拦截器生成的用户信息,用ThreadLocal.set直接放到我们的ThreadLocal中去,以便后续使用。

1.3.3带来的好处

  1. 线程安全
  2. 不需要加锁,提高执行效率
  3. 更高效的利用内存、节省开销:想对于每个任务都新建一个SimpleDateFormat
  4. 免去传参的繁琐,使得代码更优雅

2.ThreadLocal原理

  • Thread、ThreadLocal和ThreadLocalMap三者之间的关系
  • 每个Thread对象中都持有一个ThreadLocalMap成员变量
  • ThreadLocalMap可以存储多个ThreadLocal

2.1主要方法

1.T initialValue()

  • 该方法会返回当前线程对应的初始值,这是一个延迟加载的方法,只有在调用get的时候,才会触发。
  • 当线程第一次使用get方法访问变量时,将调用此方法,除非线程先前调用了set方法,在这种情况下,不会为线程调用本initialValue方法
  • 通常,每个线程最多调用一次此方法,但如果已经调用了remove0后,再调用get0,则可以再次调用此方法
  • 如果不重写本方法,这个方法会返回null。一般使用匿名内部类的方法来重写initialValue0方法,以便在后续使用中可以初始化副本对象。
public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        // 1.使用前调用了set方法
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        // 2.调用setInitialValue方法
        return setInitialValue();
    }


// set()的变量,用于建立initialValue。在用户重写了set()方法的情况下使用,而不是set()

private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }

2.set(T value) 为这个线程设一个新值

public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

3.get() 得到这个线程对应的value 

get方法是先取出当前线程的ThreadLocalMap,然后调用map.getEntry方法,把本ThreadLocal的引用作为参数传入,取出map中属于本ThreadLocal的value

public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

 4.remove() 删除对应这个线程的值

 public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }

 演示:

package cn.butool.threadlocal;

/**
 * 演示threadLocal避免传递参数的代码冗余
 */
public class UseThreadLocalSaveUser {
    public static void main(String[] args) {
        new Service1().process();
    }
}

class Service1 {
    // 读取到用户信息
    public void process() {
        User user = new User("busl");
        UserContextHolder.holder.set(user);
        new Service2().process();
    }
}

class Service2 {
    // 读取到用户信息
    public void process() {
        System.out.println("service2"+UserContextHolder.holder.get().getName());
        UserContextHolder.holder.remove();
        new Service3().process();
    }
}
class Service3 {
    // 读取到用户信息
    public void process() {
        User user = new User("busl2");
        UserContextHolder.holder.set(user);
        System.out.println("service3"+UserContextHolder.holder.get().getName());
    }
}

/**
 * 用户上下文持有者
 */
class UserContextHolder{
    public static ThreadLocal<User> holder = new ThreadLocal<>();
}
class User {
    private String name;

    public User(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

打印:

service2busl
service3busl2

2.2ThreadLocalMap

  • ThreadLocalMap类也就是ThreadLocal.threadLocals
ThreadLocal.ThreadLocalMap threadLocals = null;
  • ThreadLocalMap是每个线程Thread类里面的变量,里面最重要的是一个键值对数组Entry[] table,可以认定为一个map键值对
    键:这个ThreadLocal 值实际的成员变量,比如User或者simpleDateFormat对象
  • setInitialValue和直接set最后都是调用map.set()方法来设置值,最后都会对应到ThreadLocalMap的一个Entry,只不过起点和入口不一样

3.ThreadLocal使用注意点

3.1内存泄露

某个对象不在有用了,但是占用的内存却不能回收,会导致这一部分始终被占用,如果这种情况有很多。越来越多的情况会导致内存不够用了,超限制了。

3.2Key的泄露

ThreadLocalMap 中的内部类 Entry继承自WeakReference,是弱引用

弱引用的特点:如果这个对象只被弱引用关联,那么这个对象就可以被回收,弱引用不会阻止GC。

强引用:通常 一个对象等于什么,比如  下面的  value = v;

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

ThreadLocalMap的每个Entry都是一个对key的弱引用,和一个对value的value的强引用。

正常情况下,当线程终止,保存在Threadlocal里面的Value会被垃圾回收,因此没有任何强引用了。

但是,如果线程不终止,保持很久,那么key对应的value就不能被回收,因此 有以下调用链:

Thread -> ThreadLocalMap -> Entry(key为null) -> value

因为value 和Thread之间还存在强引用链路,所以导致value无法回收,就可能出现oom

JDK已经考虑到这个问题,所以set,remove,rehash方法中会扫描key为null的Entry,并且把对应的value设置为null,这样value就可以被对象回收,下面的考虑,把强引用链给断掉。

                   if (k == null) {
                        e.value = null; // Help the GC
                    } 

private void resize() {
            Entry[] oldTab = table;
            int oldLen = oldTab.length;
            int newLen = oldLen * 2;
            Entry[] newTab = new Entry[newLen];
            int count = 0;

            for (int j = 0; j < oldLen; ++j) {
                Entry e = oldTab[j];
                if (e != null) {
                    ThreadLocal<?> k = e.get();
                    if (k == null) {
                        e.value = null; // Help the GC
                    } else {
                        int h = k.threadLocalHashCode & (newLen - 1);
                        while (newTab[h] != null)
                            h = nextIndex(h, newLen);
                        newTab[h] = e;
                        count++;
                    }
                }
            }

            setThreshold(newLen);
            size = count;
            table = newTab;
        }

但是如果一个ThreadLocal不被使用,那么实际上set, remove,rehash方法也不会被调用,如果同时线程又不停止,那么用链就一直存在,那么就导致了value的内存泄漏

3.3如何避免内存泄露

调用remove方法,就会删除对应的Entry对象,可以避免内存泄漏。

所以使用完ThreadLocal之后,应该调用remove方法。

在实际开发中,如果使用拦截器的方法拦截保存用户信息,应该在线程请求完成前拦住,调用remove方法。

class Service3 {
    // 读取到用户信息
    public void process() {
        User user = new User("busl2");
        UserContextHolder.holder.set(user);
        System.out.println("service3"+UserContextHolder.holder.get().getName());
        // 在最后的代码使用完去remove
        UserContextHolder.holder.remove();
    }
}

 3.2ThreadLocal带来的空指针异常

package cn.butool.threadlocal;

/**
 *
 */
public class ThreadLocalNPE {
    ThreadLocal<Long> longThreadLocal = new ThreadLocal<Long>();

    public void set() {
        longThreadLocal.set(Thread.currentThread().getId());
    }

    public long get() {
        return longThreadLocal.get();
    }

    public static void main(String[] args) {
        ThreadLocalNPE threadLocalNPE = new ThreadLocalNPE();
        System.out.println(threadLocalNPE.get());
        Thread thread = new Thread(() -> {
            threadLocalNPE.set();
            System.out.println(threadLocalNPE.get());
        });
        thread.start();
    }
}

//改为Long即可,编码问题

public Long get() {
        return longThreadLocal.get();
    }

3.3共享对象

如果在每个线程中ThreadLocal.set0进去的东西本来就是多线程共享的同一个对象,比如static对象,那么多个线程的ThreadLocal.get0取得的还是这个共享对象本身,还是有并发访问问题 

3.4注意点

1.如果可以不适用ThreadLoca就解决问题,那么不要强行使用

        例如任务很少的时候,在局部变量中可以新建对象就可以解决问题,那么就不需要使用到ThreadLoca 

2.优先使用框架的支持,而不是自己的创造

        例如在Spring中,如果可以使用RequestContextHolder,那么就不需要自己维护ThreadLocal,因为自己可能会忘记调用remove()方法等,造成内存泄漏

3.5实际应用场景

DateTimeContextHolder

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.format.datetime.standard;

import java.time.format.DateTimeFormatter;
import java.util.Locale;
import org.springframework.core.NamedThreadLocal;

public final class DateTimeContextHolder {
    private static final ThreadLocal<DateTimeContext> dateTimeContextHolder = new NamedThreadLocal("DateTime Context");

    public DateTimeContextHolder() {
    }

    public static void resetDateTimeContext() {
        dateTimeContextHolder.remove();
    }

    public static void setDateTimeContext(DateTimeContext dateTimeContext) {
        if (dateTimeContext == null) {
            resetDateTimeContext();
        } else {
            dateTimeContextHolder.set(dateTimeContext);
        }

    }

    public static DateTimeContext getDateTimeContext() {
        return (DateTimeContext)dateTimeContextHolder.get();
    }

    public static DateTimeFormatter getFormatter(DateTimeFormatter formatter, Locale locale) {
        DateTimeFormatter formatterToUse = locale != null ? formatter.withLocale(locale) : formatter;
        DateTimeContext context = getDateTimeContext();
        return context != null ? context.getFormatter(formatterToUse) : formatterToUse;
    }
}

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

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

相关文章

第5章:排序与分页

1.排序数据order by 1.1 排序规则 没有使用排序操作&#xff0c;查询返回的数据是按照添加数据顺序显示 ①&#xff1a;使用order by 子句排序&#xff0c;order by 子句在select语句的结尾 asc(ascend):升序 desc(descend):降序 ②&#xff1a;按照salary从高到低的顺序…

【Linux从入门到精通】Linux常用基础指令(上)

本篇文章会对Linux下的常用进行详细解释&#xff0c;并附加实例。通俗易懂&#xff0c;希望会对你有所帮助。由于内容较多&#xff0c;分为上下篇来讲解。 文章目录 一、什么是Linux的指令呢&#xff1f; 二、Linux下的常用指令 2、1 ls 指令 2、1、1 ls -a 2、1、2 ls -l 2、…

python开发构建基于CNN的人脸识别系统

卷积神经网络在图像处理领域中早就是独树一帜的存在&#xff0c;今天正好有时间就想着基于CNN开发构建一个人脸识别系统&#xff0c;首先看下效果图&#xff1a; 数据集来源于LFW数据集&#xff0c;简单看下本文使用的小批量的数据集如下&#xff1a; 一共有12个人的图像数据&a…

详解git(工作必备)

基本概念 Git 是一个开源的分布式版本控制系统&#xff0c;用以有效、高速的处理从很小到非常大的项目版本管理。 Git 的作用与好处是&#xff1a; 可以帮我们保存文件的所有修改记录&#xff0c;并且使用版本号进行区分&#xff0c;让我们随时可以浏览历史版本、对比不同版本…

Mysql列的完整性约束详解(设置表字段的非空约束(NOT NULL, NK))

设置表字段的非空约束&#xff08;NOT NULL, NK&#xff09; 文章目录 系列文章目录一、设置表字段的非空约束&#xff08;NOT NULL, NK&#xff09; 1.简介2.语句格式总结 一、设置表字段的非空约束&#xff08;NOT NULL, NK&#xff09; 1.简介 当数据库表中的某个字段上的…

perf工具报错,升级ubuntu子系统linux内核

文章目录 1&#xff0c;运行perf工具报错1.1&#xff0c;可能的原因有&#xff1a; 2&#xff0c;我选择升级linux内核&#xff0c;和当前perf工具版本保持一致2.1&#xff0c;下载6.2.12内核源码2.2&#xff0c;安装6.2.12内核 1&#xff0c;运行perf工具报错 1.1&#xff0c;…

今天国际医药行业大型物流系统集成建设案例——广州医药智慧物流中心项目

请点击↑关注、收藏&#xff0c;本博客免费为你获取精彩知识分享&#xff01;有惊喜哟&#xff01;&#xff01; 近几年&#xff0c;带量采购常态化、制度化、医保支付方式改革、医保谈判药品“双通道”、线上购药医保结算等一系列政策加速药品流通行业变革步伐&#xff0c;由…

数据驱动的皮肤病变分类深度监测

文章目录 Data-Driven Deep Supervision for Skin Lesion Classification摘要本文方法Layer-Wise Effective Receptive Field (LERF)Object Size Approximation Using Activation MappingDeep Supervision Employment 实验结果 Data-Driven Deep Supervision for Skin Lesion C…

基于html+css的图展示33

准备项目 项目开发工具 Visual Studio Code 1.44.2 版本: 1.44.2 提交: ff915844119ce9485abfe8aa9076ec76b5300ddd 日期: 2020-04-16T16:36:23.138Z Electron: 7.1.11 Chrome: 78.0.3904.130 Node.js: 12.8.1 V8: 7.8.279.23-electron.0 OS: Windows_NT x64 10.0.19044 项目…

15天学习MySQL计划-SQL优化/视图(进阶篇)-第八天

SQL优化 1.插入数据&#xff08;insert&#xff09; 1.批量插入 insert into 表名 values(值1,值2),(值1,值2),(值1,值2),(值1,值2) 2.手动提交事务 --查看当前提交的状态 select autocommit; --0代表手动提交 1代表自动提交 set autocommit 0; ​ commit #提交事务 3…

【计算机网络 - 第二章】应用层(更新中)

目录 一、应用层协议原理 1、网络应用程序体系结构 &#xff08;1&#xff09;客户/服务器模型&#xff08;C/S&#xff09; &#xff08;2&#xff09;P2P模型&#xff08;Peer-to-Peer&#xff09; 2、进程通信 &#xff08;1&#xff09;客户和服务器进程 &#xff0…

重载是什么?只有返回值类型不同算不算重载?为什么?

重载定义 首先&#xff0c;重载&#xff0c;从简单说&#xff0c;就是函数或者方法有相同的函数名&#xff0c;但是参数列表不相同的情形&#xff0c;这样的同名不同参数的函数或者方法之间&#xff0c;互相称之为重载函数或者重载方法&#xff0c;例子如下&#xff1a; 重载但…

机房一体化监测解决方案

随着5G、工业互联网等最新基建项目的扎实推进&#xff0c;我国数字经济发展也在不断开拓&#xff0c;很多地区也在积极建设新型信息基础设施&#xff0c;围绕规划、网络、用能等方面加大支持力度。在信息网络技术不断发展的背景下&#xff0c;各类网络设备机房广泛分布。但很多…

品牌商业笔记怎么写,投放笔记总结

小红书的用户当中有百分之五十以上都是来自一线城市&#xff0c;这证明着平台蕴藏着巨大的流量价值。这其中&#xff0c;品牌为了利益自然会去追求如何写商业笔记。今天就来和大家一起分享一下品牌商业笔记怎么写&#xff0c;在投放笔记的时候需要注意什么呢。 根据最新的数据显…

【22-23 春学期】人工智能基础--AI作业2-监督学习

【22-23 春学期】AI作业2-监督学习_HBU_David的博客-CSDN博客 用自己的语言&#xff0c;解释以下概念 1 结构风险最小化 2 正则化 3 线性回归 4 逻辑斯蒂回归 5 Sigmoid 与 SoftMax 函数 6 决策树 7 信息熵 条件熵 信息增益 8 线性判别分析 LDA 9 概率近似正确 PAC …

TCP分岔:优化云服务的性能

云服务器问题 对于搜索、电子邮件和社交网络等云服务&#xff0c;非常需要提供高水平的响应性&#xff0c;因此这些服务运行在自己的端系统&#xff08;包括其智能手机&#xff09;中 由于用户经常位于远离数据中心的地方&#xff0c;而这些数据中心负责为云服务管理的动态内…

刷爆朋友圈!前百度总裁陆奇最新AI重磅演讲:我的大模型世界观

文 / 高扬 陆奇的演讲刷屏了&#xff0c;我认真看了他的演讲稿&#xff0c;收获颇丰。 陆奇提到&#xff0c;人类社会的发展&#xff0c;大致可分为&#xff1a;农业化、工业化和数字化三个阶段。 在三个递进的阶段中&#xff0c;人类一直在探索如何减少烦琐且消耗能量的体力和…

紧密联结玩家 | 2023 Google 游戏开发者峰会

玩家的选择是对游戏莫大的认可&#xff0c;重视玩家反馈并和他们建立联系是您的游戏取得成功的关键。我们也在努力创造更多机会&#xff0c;让您的游戏从琳琅满目的列表中脱颖而出&#xff0c;帮助您吸引更多用户。 上篇内容我们介绍了帮助您优化游戏性能的几大功能更新&#x…

【Anaconda】更换清华源实现pip提速

Anaconda用pip安装package时&#xff0c;经常会遇到安装过慢或安装失败的情况&#xff0c;使用以下方法&#xff0c;可以实现package的快速安装。 目录 一、修改配置文件 1.1 生成生成.condarc文件 1.2 修改~/.condarc文件&#xff0c;写入源文件 1.3 配置成功 二、通过A…

中国的chatGpt-中国chatGPT软件

chatGPT中文免费版 您是否在寻找一款免费且实用的聊天软件来更好地与别人交流&#xff1f;那么&#xff0c;“chatGPT中文免费版”将是您的不二选择&#xff01; 作为一款由 OpenAI 训练的大型语言模型&#xff0c;chatGPT 中文免费版可以让您轻松地与其他人进行交流&#xf…