JavaWeb14-线程池

news2024/10/6 6:43:09

目录

1.传统线程的缺点

2.线程池的定义

3.线程池的优点

4.线程池的创建/使用(2类7种)

4.1.通过Executors(执行器)自动创建(6种)

①Executors.newFixedThreadPool:创建⼀个固定⼤⼩的线程池,可控制并发的线程数,超出的线程会在队列中等待。

--->PS:submit VS execute

--->PS:有返回值的线程池实现

--->PS:线程池中的线程工厂

②Executors.newCachedThreadPool:创建⼀个可缓存的线程池,若线程数超过处理所需,缓存⼀段时间后会回收,若线程数量不够,则新建线程,线程数随任务量而定(前提CPU性能好)。

③Executors.newSingleThreadExecutor:创建单个线程数的线程池,它可以保证先进先出的执⾏顺序。

--->PS:(常见面试题)newSingleThreadExecutor:创建单个线程数的线程池,那为何不直接使用线程?单线程线程池的意义?

④Executors.newScheduledThreadPool:创建⼀个可以执⾏延迟任务的线程池。

--->a.延迟执行一次

--->b.固定频率执行scheduleAtFixedRate

--->c.固定频率执行scheduleWithFixedDelay

 --->PS:scheduleAtFixedRate VS scheduleWithFixedDelay

⑤Executors.newSingleThreadScheduledExecutor:创建⼀个单线程的可以执行延迟任务的线程池。

⑥Executors.newWorkStealingPool:根据当前服务器的CPU创建⼀个抢占式执⾏的线程池(任务执⾏顺序不确定)【JDK 1.8 添加】。

4.2.通过ThreadPoolExecutor手动创建(1种)

⑦ThreadPoolExecutor:重点掌握,最原始方式,推荐使用。

--->Executors自动创建线程池返回的线程池对象的弊端如下:

--->a.ThreadPoolExecutor 参数说明(包含 7 个参数可供设置,最少需要设置5个参数)

--->b.线程池执行流程

--->c.线程池拒绝策略(4【JDK提供】+1【自定义】)

5.线程池状态(5种)

①RUNNING:

②SHUTDOWN:

③STOP:

④TIDYING:

⑤TERMINATED:

a.各个状态的转换过程

b.shutdown VS shutdownNow

6.究竟选用哪种线程池?


1.传统线程的缺点

  • 有任务时创建线程,没任务时结束线程:创建线程需要开辟本地线程栈、虚拟机栈、程序计数器等私有线程内存,消耗的时候也需要释放这些内存。频繁地创建和销毁需要⼀定的开销。

  • 线程没有任务队列的任务管理功能:当任务数远远⼤于线程可以承载的数量之后,不能友好地进⾏任务拒绝。

2.线程池的定义

线程池(ThreadPool)是⼀种基于池化思想管理和使⽤线程的机制。

它是将多个线程预先存储在⼀个“池⼦”内,当有任务出现时可以避免重新创建和销毁线程所带来性能开销,只需要从“池⼦”内取出相应的线程执⾏对应的任务即可。

池化思想在计算机的应⽤也⽐较⼴泛:

  • 内存池(Memory Pooling):预先申请内存,提升申请内存速度,减少内存碎⽚。

  • 连接池(Connection Pooling):预先申请数据库连接,提升申请连接的速度,降低系统的开销。

  • 实例池(Object Pooling):循环使⽤对象,减少资源在初始化和释放时的昂贵损耗。

3.线程池的优点

  1. 复用线程:避免线程重复创建和销毁的性能开销。
  2. 提⾼响应速度:任务到达时,⽆需等待线程创建即可⽴即执⾏。
  3. 控制线程数量:避免因线程创建过多而导致OOM(out of memory内存溢出)情况。
  4. 提供内存管理功能:可实现任务缓存和任务拒绝。
  5. 提供更多功能:比如定时任务,允许任务延期执⾏或定期执⾏。

同时阿⾥巴巴在其《Java开发⼿册》中也强制规定:线程资源必须通过线程池提供,不允许在应⽤中⾃⾏显式创建线程。

4.线程池的创建/使用(2类7种)

4.1.通过Executors(执行器)自动创建(6种)

Executors.newFixedThreadPool:创建⼀个固定⼤⼩的线程池,可控制并发的线程数,超出的线程会在队列中等待。

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

/**
 * 创建一个固定大小的线程池
 */
public class ThreadPoolDemo1 {
    public static void main(String[] args) {
        //1.创建了一个包含5个线程的线程池
        ExecutorService threadPool = Executors.newFixedThreadPool(5);

        //2.使用submit线程池执行任务一
        for (int i = 0; i < 5; i++) {
            //给线程池添加任务
            threadPool.submit(new Runnable() { //匿名内部类
                @Override
                public void run() {
                    System.out.println("线程名称:" + Thread.currentThread().getName());
                }
            });
        }

        //2.使用execute线程池执行任务二
        for (int i = 0; i < 10; i++) {
            //给线程池添加任务
            threadPool.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("线程名称:" + Thread.currentThread().getName());
                }
            });
        }
    }
}

--->PS:submit VS execute

 

  • submit:既支持有返回值,也支持无返回值。(推荐使用)
  • execute:只支持无返回值。

--->PS:有返回值的线程池实现

import java.util.Random;
import java.util.concurrent.*;

/**
 * 有返回值的线程池
 */
public class ThreadPoolDemo2 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService threadPool = Executors.newFixedThreadPool(5);

        Future<Integer> result = threadPool.submit(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception{
                int num = new Random().nextInt(10);
                System.out.println("生成随机数:" + num);
                return num;
            }
        });

        System.out.println("得到线程池返回结果:" + result.get()); //会有阻塞,会等到拿到返回值之后再去执行后面的代码
    }
}

上面代码是使用默认的线程工厂。

--->PS:线程池中的线程工厂

作用:为线程池提供线程的创建。

提供的功能:

  1. 设置线程池中的线程的命名规则。
  2. 设置线程的优先级。
  3. 设置线程的分组。
  4. 设置线程的类型(用户线程、守护/后台线程)。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import static java.lang.Thread.MAX_PRIORITY;

/**
 *线程工厂示例演示
 */
public class ThreadPoolDemo3 {
    public static void main(String[] args) {
        //创建线程工厂
        ThreadFactory factory = new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                //!!!一定要注意要把任务Runnable设置给新创建的线程
                Thread thread = new Thread(r);
                //设置线程的命名规则
                thread.setName("我的线程-" + r.hashCode());
                //设置线程的优先级
                thread.setPriority(MAX_PRIORITY);
                return thread;
            }
        };

        ExecutorService service = Executors.newFixedThreadPool(5,factory);
        for (int i = 0; i < 5; i++) {
            service.submit(() -> {
                //任务
                Thread thread = Thread.currentThread();
                System.out.println("线程池开始执行了:" + thread.getName() + ",线程优先级:" + thread.getPriority());
            });
        }
    }
}

线程池里的线程永远处于存活状态,不会自动停止,除非调用线程池终止执行方法。

Executors.newCachedThreadPool:创建⼀个可缓存的线程池,若线程数超过处理所需,缓存⼀段时间后会回收,若线程数量不够,则新建线程,线程数随任务量而定(前提CPU性能好)。

优点:更多任务数量产⽣相应的线程池。 当有突发的大量的任务时,建议使用此方式。

缺点:占⽤资源数量⽐较多。

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

/**
 * 带缓存的线程池
 */
public class ThreadPoolDemo4 {
    public static void main(String[] args) {
        //创建线程池
        ExecutorService service = Executors.newCachedThreadPool();
        for (int i = 0; i < 1000; i++) {//任务数量总共有1000个,最终执行结果 线程数量大概是300多个
            //i必须定义一个参数,才能去使用,必须是一个确定的参数
            int finalI = i;
            service.submit(() -> {
                System.out.println("i:" + finalI + "|线程名称" + Thread.currentThread().getName());
            });
        }
    }
}

若不关闭,会一直执行下去。

Executors.newSingleThreadExecutor:创建单个线程数的线程池,它可以保证先进先出的执⾏顺序。

--->PS:(常见面试题)newSingleThreadExecutor:创建单个线程数的线程池,那为何不直接使用线程?单线程线程池的意义?

  1. 复用线程。
  2. 单线程的线程池提供了任务队列和拒绝策略(当任务队列满了Integer.MAX_VALUE之后,新来的任务就会执行拒绝策略)。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolDemo7 {
    public static void main(String[] args) {
        ExecutorService service = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 10; i++) {
            int finalI = i;
            service.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("任务:" + finalI + "线程名:" + Thread.currentThread().getName());
                }
            });
        }
    }
}

Executors.newScheduledThreadPool:创建⼀个可以执⾏延迟任务的线程池。

--->a.延迟执行一次

import java.time.LocalDateTime;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 创建执行定时任务的线程池
 */
public class ThreadPoolDemo5 {
    public static void main(String[] args) {
        //创建线程池
        ScheduledExecutorService service = Executors.newScheduledThreadPool(5);
        System.out.println("添加任务的时间:" + LocalDateTime.now());
        //执行一次的定时任务
        scheduleTest(service);
    }
    
    /**
     * 执行一次的定时任务
     * @param service
     */
    private static void scheduleTest(ScheduledExecutorService service) {
        //执行定时任务(延迟3秒执行)。这个延迟任务只能执行一次,不能继续执行。
        //延迟执行一次定时任务
        service.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("执行了任务:" + LocalDateTime.now());//参数1:执行的任务
            }
        },3, TimeUnit.SECONDS);//参数2:延迟多久进行执行; 参数3:是参数2的时间单位描述
    }
}

--->b.固定频率执行scheduleAtFixedRate

import java.time.LocalDateTime;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 创建执行定时任务的线程池
 */
public class ThreadPoolDemo5 {
    public static void main(String[] args) {
        //创建线程池
        ScheduledExecutorService service = Executors.newScheduledThreadPool(5);
        System.out.println("添加任务的时间:" + LocalDateTime.now());

        //2s之后开始执行定时任务,定时任务每隔4s执行一次
        service.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println("执行任务:" + LocalDateTime.now());
            }
        },2,4, TimeUnit.SECONDS);
    }
}

--->c.固定频率执行scheduleWithFixedDelay

import java.time.LocalDateTime;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 创建执行定时任务的线程池
 */
public class ThreadPoolDemo5 {
    public static void main(String[] args) {
        //创建线程池
        ScheduledExecutorService service = Executors.newScheduledThreadPool(5);
        System.out.println("添加任务的时间:" + LocalDateTime.now());

        //2s之后开始执行定时任务,定时任务每隔4s执行一次
        service.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                System.out.println("执行时间:" + LocalDateTime.now());
            }
        },2,4,TimeUnit.SECONDS);
    }
}

 --->PS:scheduleAtFixedRate VS scheduleWithFixedDelay

①scheduleAtFixedRate 是以上⼀次任务的开始时间,作为下次定时任务的参考时间的(参考时间+延迟任务=任务执⾏)。

import java.time.LocalDateTime;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 创建执行定时任务的线程池
 */
public class ThreadPoolDemo5 {
    public static void main(String[] args) {
        //创建线程池
        ScheduledExecutorService service = Executors.newScheduledThreadPool(5);
        System.out.println("添加任务的时间:" + LocalDateTime.now());

        //2s之后开始执行定时任务,定时任务每隔4s执行一次
        service.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println("执行任务:" + LocalDateTime.now());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },2,4, TimeUnit.SECONDS);
    }
}

注意:

如果任务执⾏时间⼤于延迟任务设定的间隔时间,则会以任务执行时间为定时任务间隔周期来执行,即哪个值大就用哪个值作为定时任务间隔周期。

public class ThreadPoolDemo5 {
    public static void main(String[] args) {
        //创建线程池
        ScheduledExecutorService service = Executors.newScheduledThreadPool(5);
        System.out.println("添加任务的时间:" + LocalDateTime.now());

        //2s之后开始执行定时任务,定时任务每隔3s执行一次
        service.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println("执行任务:" + LocalDateTime.now());
                try {
                    Thread.sleep(4000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },2,3, TimeUnit.SECONDS);
    }
}

②scheduleWithFixedDelay 是以上⼀次任务的结束时间,作为下次定时任务的参考时间的。

public class ThreadPoolDemo5 {
    public static void main(String[] args) {
        //创建线程池
        ScheduledExecutorService service = Executors.newScheduledThreadPool(5);
        System.out.println("添加任务的时间:" + LocalDateTime.now());

        //2s之后开始执行定时任务,定时任务每隔4s执行一次
        service.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                System.out.println("执行时间:" + LocalDateTime.now());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },2,4,TimeUnit.SECONDS);
    }
}

Executors.newSingleThreadScheduledExecutor:创建⼀个单线程的可以执行延迟任务的线程池。

newSingleThreadScheduledExecutor是newScheduledThreadPool的单线程版本。

import java.time.LocalDateTime;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ThreadPoolDemo6 {
    public static void main(String[] args) {
        //创建执行定时任务的单线程线程池
        ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
        System.out.println("添加任务的时间:" + LocalDateTime.now());
        service.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("执行任务:" + LocalDateTime.now());
            }
        },2, TimeUnit.SECONDS);
    }
}

Executors.newWorkStealingPool:根据当前服务器的CPU创建⼀个抢占式执⾏的线程池(任务执⾏顺序不确定)【JDK 1.8 添加】。

优点:智能、高效。

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

public class ThreadPoolDemo8 {
    public static void main(String[] args) {
        //根据当前设备的配置自动生成线程池
        ExecutorService service = Executors.newWorkStealingPool();

        for (int i = 0; i < 100; i++) {
            service.submit(() -> {
                System.out.println("线程名:" + Thread.currentThread().getName());
            });
        }

        while(!service.isTerminated()){
        }
    }
}

4.2.通过ThreadPoolExecutor手动创建(1种)

⑦ThreadPoolExecutor:重点掌握,最原始方式,推荐使用。

--->Executors自动创建线程池返回的线程池对象的弊端如下:

  1. FixedThreadPool和SingleThreadPool:允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM。
  2. CachedThreadPool:允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。

OOM代码演示:

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

/**
 * 演示OOM
 */
public class ThreadPoolDemo9 {
    static class OOMClass{
        //1M空间(M KB Byte)
        private byte[] bytes = new byte[1 * 1024 * 1024];
    }

    public static void main(String[] args) {
        ExecutorService service = Executors.newCachedThreadPool();
        Object[] objects = new Object[15];

        for (int i = 0; i < 15; i++) {
            int finalI = i;
            service.execute(() -> {
                try {
                    Thread.sleep(finalI * 200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                OOMClass oomClass = new OOMClass();
                objects[finalI] = oomClass;
                System.out.println("执行第" + finalI + "次");
            });
        }
    }
}

关于参数设置

  • -XX:标准设置,所有 HotSpot 都⽀持的参数。
  • -X:⾮标准设置,特定的 HotSpot 才⽀持的参数。
  • -D:程序参数设置,-D参数=value,程序中使⽤:System.getProperty("获取")。
  • mx 是 memory max 的简称。

《阿里巴巴Java开发手册》中强制规定:线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样让程序员更加明确线程池的运行规则,规避资源耗尽的风险。

import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 手动方式创建线程池
 */
public class ThreadPoolDemo10 {
    public static void main(String[] args) {
        ThreadFactory factory = new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
              Thread thread = new Thread(r);
              return thread;
            }
        };

        //手动方式创建线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 10, TimeUnit.SECONDS, new LinkedBlockingDeque<>(100), factory, new ThreadPoolExecutor.DiscardPolicy());

        //设置任务
        for (int i = 0; i < 10; i++) {
            executor.submit(() -> {
                System.out.println("线程名称:" + Thread.currentThread().getName());
            });
        }

        //终止线程
        executor.shutdown();//当任务执行完直接就结束了
    }
}

--->a.ThreadPoolExecutor 参数说明(包含 7 个参数可供设置,最少需要设置5个参数)

  1. corePoolSize:核心线程数(正式员工的数量),可以⼤致理解为⻓期驻留的线程数⽬(除⾮设置了allowCoreThreadTimeOut)。对于不同的线程池,这个值可能会有很⼤区别,⽐如 newFixedThreadPool 会将其设置为 nThreads,⽽对于 newCachedThreadPool 则是为0

  2. maximumPoolSize:最大线程数(正式员工+临时员工的数量),就是线程不够时能够创建的最⼤线程数。同样进⾏对⽐,对于newFixedThreadPool,当然就是 nThreads,因为其要求是固定⼤⼩,⽽ newCachedThreadPool 则是 Integer.MAX_VALUE。

  3. keepAliveTime:空闲线程的保活时间(针对临时员工),如果线程的空闲时间超过这个值,那么将会被关闭。注意此值⽣效条件必须满⾜:空闲时间超过这个值,并且线程池中的线程数少于等于核⼼线程数 corePoolSize。当然核⼼线程默认是不会关闭的,除⾮设置了allowCoreThreadTimeOut(true)那么核⼼线程也可以被回收。

  4. TimeUnit:对参数3的时间单位描述

  5. BlockingQueue:任务/阻塞队列,⽤于存储线程池的待执⾏任务。必须要设置参数值;若不设置参数值,默认为Integer.MAX_VALUE,也会导致OOM问题。

  6. threadFactory:用于生成线程的线程工厂,可设置线程属性,⼀般我们可以⽤默认的就可以了。

  7. handler:拒绝策略管理器,处理极端问题。当线程池已经满了,但是又有新的任务提交的时候,该采取什么策略由这个来指定。有⼏种⽅式可供选择,像抛出异常、直接拒绝然后返回等,也可以⾃⼰实现相应的接⼝实现⾃⼰的逻辑。

--->b.线程池执行流程

--->c.线程池拒绝策略(4【JDK提供】+1【自定义】)

JDK提供的4种:

①AbortPolicy(默认的拒绝策略):提示异常,拒绝执行。

②DiscardPolicy:忽略最新的任务。

③DiscardOldestPolicy:忽略旧任务(任务队列中的第一个任务)。

④CallerRunsPolicy:使用调用线程池的线程来执行任务~叫救援。

自定义的1种:

5.线程池状态(5种)

RUNNING:

线程池创建之后的初始状态,这是最正常的状态:接受新的任务,处理等待队列中的任务。

②SHUTDOWN:

线程池不再接受新的任务提交,但是会继续处理等待队列中的任务,将其执行结束。

STOP:

线程池不接受新的任务提交,不再处理等待队列中的任务,中断正在执⾏任务的线程。

TIDYING:

该状态下所有的任务都销毁了,workCount 为 0。会执⾏钩⼦⽅法 terminated()。

TERMINATED:

terminated() ⽅法结束后,线程池的状态就会变成这个。

a.各个状态的转换过程

  • RUNNING -> SHUTDOWN:当调⽤了 shutdown() 后,会发⽣这个状态转换,这也是最重要的;
  • (RUNNING or SHUTDOWN) -> STOP:当调⽤ shutdownNow() 后,会发⽣这个状态转换;
  • SHUTDOWN -> TIDYING:当任务队列和线程池都清空后,会由 SHUTDOWN 转换为 TIDYING;  
  • STOP -> TIDYING:当任务队列清空后,发⽣这个转换;
  • TIDYING -> TERMINATED:当 terminated() ⽅法结束后。

b.shutdown VS shutdownNow

  • shutdown 执⾏时线程池终⽌接收新任务,并且会将任务队列中的任务处理完;
  • shutdownNow 执⾏时线程池终⽌接收新任务,并且会终⽌执⾏任务队列中的任务。

6.究竟选用哪种线程池?

阿⾥巴巴《Java开发⼿册》给的答案:推荐使⽤ThreadPoolExecutor 的⽅式进⾏线程池的创建,因为这种创建⽅式更可控,并且更加明确了线程池的运⾏规则,可以规避⼀些未知的⻛险。

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

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

相关文章

哈希冲突

为什么会有哈希冲突&#xff1f;哈希表通过哈希函数来计算存放数据&#xff0c;在curd数据时不用多次比较&#xff0c;时间复杂度O&#xff08;1&#xff09;。但是凡事都有利弊&#xff0c;不同关键字通过相同哈希函数可能计算出来相同的存放地址&#xff0c;这种现象被称为哈…

JVM的内存回收及常见算法

什么样的对象应该被回收&#xff1f;某个对象不再被栈直接或间接地引用&#xff0c;此时就应该被回收了。o被指向null的时候&#xff0c;new Object()创建的对象就不在被栈引用了&#xff0c;可以被回收。p1和personList均不再指向第一个Person对象的时候&#xff0c;第一个Per…

【小墩墩学Android】开发常见问题FAQ之Gradle更新

文章目录1、简介1.1 Android简介1.2 Gradle简介1.3 Gradle的配置文件1.3.1 应用模块的 build.gradle1.3.2 项目的 settings.gradle1.3.3 gradle-wrapper.properties2、Gradle文件下载失败2.1 手动下载gradle2.2 配置本地gradle2.3 配置国内镜像3、repositories配置国内源3.1 单…

蓝桥杯三月刷题 第八天

文章目录&#x1f4a5;前言&#x1f609;解题报告&#x1f4a5;分数&#x1f914;一、思路:&#x1f60e;二、代码&#xff1a;&#x1f4a5;回文日期&#x1f914;一、思路:&#x1f60e;二、代码&#xff1a;&#x1f4a5;迷宫&#x1f914;一、思路:&#x1f60e;二、代码&a…

LVGL学习笔记18 - 表Table

目录 1. Parts 1.1 LV_PART_MAIN 1.2 LV_PART_ITEMS 2. 样式 2.1 设置行列数 2.2 设置单元格字符串 2.3 设置单元格宽度 2.4 设置表格高度和宽度 2.5 设置字符串颜色 2.6 设置边框颜色 2.7 设置背景颜色 3. 事件 4. CELL CTRL 表格是由包含文本的行、列和单元格构…

【Git】Git仓库初始化

Git本地仓库初始化 1.将本地代码上传至远程新建仓库 1.1.建立远程仓库 1.2.初始化本地代码仓库 第一步&#xff1a;进入本地代码目录 cd /代码路径 第二步&#xff1a;初始化仓库(执行如下命令) git init 第三步&#xff1a;将本地全部文件添加到本地缓冲区(执行如下命令)…

循环神经网络原理及实现(二):循环神经网络复现

专栏&#xff1a;神经网络复现目录 循环神经网络 循环神经网络&#xff08;Recurrent Neural Network&#xff0c;RNN&#xff09;是一种神经网络结构&#xff0c;其主要特点是网络中存在循环连接&#xff0c;使得网络具有记忆功能&#xff0c;可以处理序列数据。在传统神经网…

autoxjs

文章目录autojs一、工具二、使用步骤1.手机设置开发模式并打开usb调试2.安装scrcpy3. 安装autoxjs4. vscode插件使用auto 入门语法总结autojs autojs 目前作者已经跑路了&#xff0c;转为用社区的autoxjs&#xff0c;官网地址&#xff1a;http://doc.autoxjs.com/#/ 一、工具 …

Echart的使用初体验,Echarts的基本使用及语法格式,简单图表绘制和使用及图例添加【学习笔记】

Echart&#xff1f; ECharts 是一个使用 JavaScript 实现的开源可视化库&#xff0c;涵盖各行业图表&#xff0c;满足各种需求。 ECharts 遵循 Apache-2.0 开源协议&#xff0c;免费商用。 ECharts 兼容当前绝大部分浏览器&#xff08;IE8/9/10/11&#xff0c;Chrome&#xf…

【打造家庭服务器系列01】无桌面版Ubuntu 22.04 连接wifi

一、背景 最近有一台笔记本一直放在哪没用了&#xff0c;就想着拿来做个服务器用吧。 如何安装Ubuntu系统&#xff0c;大家可以百度搜索一下很多。 主要分三步&#xff1a; 制作U盘启动盘&#xff08;推荐使用rufus工具&#xff0c;轻量方便&#xff09;设置BIOS引导 &#x…

java——代理

什么是代理&#xff1a; 给目标对象一个代理对象&#xff0c;由代理对象控制着对目标对象的引用 为什么使用代理&#xff1a; ①&#xff1a;功能增强&#xff1a;通过代理业务对原有业务进行增强 ②&#xff1a;用户只能同行过代理对象间接访问目标对象&#xff0c;防止用…

About What Is a DBA?

1.Evaluating a DBA Job Offer Here are some useful questions to ask: • Does the company offer regular training for its DBAs to learn new DBMS features and functionality? What about training for related technologies such as programming, networking, e-bus…

[NIPS 2017] Improved Training of Wasserstein GANs (WGAN-GP)

Contents IntroductionDifficulties with weight constraintsCapacity underuseExploding and vanishing gradientsGradient penaltyReferencesIntroduction WGAN 增加了 GAN 模型训练的稳定性,但有时仍然会有生成质量不高或难以收敛的问题。作者发现上述问题经常是由 WGAN 中…

保障信息安全:使用PyZbar库识别二维码图片可以快速获取二维码中的信息,保障信息安全。

目录 简介&#xff1a; 源代码&#xff1a; 源代码说明&#xff1a; 效果如下所示&#xff1a; 简介&#xff1a; 不用摄像头识别二维码可以应用在以下场景&#xff1a; 批量处理二维码图片&#xff1a;可以在服务器上使用PyZbar等库来批量处理二维码图片&#xff0c;例如读…

Nginx 配置实例-负载均衡

一、实现效果 浏览器地址栏输入地址 http://192.168.137.129/edu/a.html&#xff0c;负载均衡效果&#xff0c;将请求平均分配到8080和8081两台服务器上。 二、准备工作 1. 准备两台tomcat服务器&#xff0c;一台8080&#xff0c;一台8081 (具体操作如下两个链接) Nginx配置实…

亚信科技新“三驾马车”再创佳绩,与数字经济同频共振

‍数据智能产业创新服务媒体——聚焦数智 改变商业近日&#xff0c;亚信科技公布了2022年财报。财报显示&#xff0c;2022年&#xff0c;亚信科技实现营收77.38亿元&#xff0c;同比上升12.2%&#xff1b;毛利润29.39亿元&#xff0c;同比上升11.1%&#xff0c;毛利率达38.0%&…

分布式链路追踪组件skywalking介绍

SkyWalking组件概念 一个开源的可观测平台, 用于从服务和云原生基础设施收集, 分析, 聚合及可视化数据。SkyWalking 提供了一种简便的方式来清晰地观测分布式系统, 甚至横跨多个云平台。SkyWalking 更是一个现代化的应用程序性能监控(Application Performance Monitoring)系统…

通过Session共享数据验证码进行用户登录

通过Session共享数据验证码进行用户登录 需求&#xff1a; 访问带有验证码的登录页面login.jsp。用户输入用户名&#xff0c;密码以及验证码。 ①。如果用户名和密码输入有误&#xff0c;跳转登陆页面&#xff0c;提示&#xff1a;用户名或密码错误。 ②。如果验证码输入有误…

核方法(kernel Method)

核方法 核方法定义 一种能够将在原始数据空间中的非线性数据映射到高维线性可分的方法。 核方法的用处 1、低维数据非线性&#xff0c;当其映射到高维空间&#xff08;feature space&#xff09;时&#xff0c;可以用线性方法对数据进行处理。 2、线性学习器相对于非线性学…

从MySQL innoDB的特性Doublewrite buffer谈起

文章目录前言什么是Doublewrite buffer为什么要叫它Doublewrite呢&#xff0c;双写分别是哪两次写&#xff0c;体现在了什么地方呢为什么需要Doublewrite bufferDoublewrite buffer的具体使用1.假如还没有进行第一次写的时候crash了&#xff0c;也就是Doublewrite buffer和磁盘…