学习JavaEE的日子 Day32 线程池

news2024/12/25 23:57:47

Day32 线程池

1.引入

一个线程完成一项任务所需时间为:

  1. 创建线程时间 - Time1
  2. 线程中执行任务的时间 - Time2
  3. 销毁线程时间 - Time3

2.为什么需要线程池(重要)

  1. 线程池技术正是关注如何缩短或调整Time1和Time3的时间,从而提高程序的性能。项目中可以把Time1,T3分别安排在项目的启动和结束的时间段或者一些空闲的时间段
  2. 线程池不仅调整Time1,Time3产生的时间段,而且它还显著减少了创建线程的数目,提高线程的复用率
  3. 系统启动一个新线程的成本是比较高的,因为涉及与操作系统的交互,在这种情形下,使用线程池可以很好地提高性能,尤其是当程序中需要创建大量生存期很短暂的线程时,优先考虑使用线程池

3.Java提供的线程池(了解即可)

一般不会使用这个,局限性太多

ExecutorService:线程池的接口

Executors:创建各种线程池的工具类

public class Test {
	
	public static void main(String[] args) {
		
		//创建单个线程的线程池
		//ExecutorService pool = Executors.newSingleThreadExecutor();
		//创建指定线程的线程池
		//ExecutorService pool = Executors.newFixedThreadPool(3);
		//创建可缓存线程的线程池,自动回收60s闲置线程
		ExecutorService pool = Executors.newCachedThreadPool();
		
		//循环创建任务对象,并提交给线程池
		for (int i = 1; i <= 100; i++) {
			//创建任务对象
			Task task = new Task(i);
			//提交任务
			pool.execute(task);
		}
		
		//关闭线程池
		pool.shutdown();
	}
}

class Task implements Runnable{
	private int i;
	public Task(int i) {
		this.i = i;
	}
	@Override
	public void run() {
		System.out.println(Thread.currentThread().getName() + "处理了第" + num + "个任务");
	}
}

4.深入源码

ExecutorService pool = Executors.newSingleThreadExecutor();

ExecutorService pool = Executors.newFixedThreadPool(3);

ExecutorService pool = Executors.newCachedThreadPool();

三种线程池底层都是ThreadPoolExecutor类的对象

-- 分析ThreadPoolExecutor类的构造方法源码--------------------------------
public ThreadPoolExecutor(
	int corePoolSize,		------------- 核心线程数量 
    int maximumPoolSize,	------------- 最大线程数量 
	long keepAliveTime,		------------- 闲置时间,作用于核心线程数与最大线程数之间的线程
	TimeUnit unit,			------------- keepAliveTime的时间单位(可以是毫秒、秒....)
	BlockingQueue<Runnable> workQueue, -- 任务队列
	ThreadFactory threadFactory, -------- 线程工厂
	RejectedExecutionHandler handler ---- 达到了线程界限和队列容量时的处理方案(拒绝策略)
) {}
执行步骤:
	1.创建线程池后
    2.任务提交后,查看是否有核心线程:
        3.1 没有 -> 就创建核心线程 -> 执行任务 -> 执行完毕后又回到线程池中
        3.2-> 查看是否有闲置核心线程:
        	4.1-> 执行任务 -> 执行完毕后又回到线程池
        	4.2 没有 -> 查看当前核心线程数是否核心线程数量:
        		5.1-> 就创建核心线程 -> 执行任务 -> 执行完毕后又回到线程池中
        		5.2-> 查看任务列表是否装载满:
        			6.1 没有 -> 就放入列表中,等待出现闲置线程
        			6.2 装满 -> 查看是否有普通线程(核心线程数到最大线程数量之间的线程)
        				7.1 没有 -> 就创建普通线程 -> 执行任务 -> 执行完毕后又回到线程池中
        				7.2-> 查看是否有闲置普通线程
        					7.1.1-> 执行任务 -> 执行完毕后又回到线程池中
        					7.1.2 没有 -> 查看现在所有线程数量是否为最大线程数:
        						8.1-> 执行处理方案(默认处理抛出异常)
        						8.2->就创建普通线程-> 执行任务 -> 执行完毕后又回到线程池中     			
注:
	1.为了更好的理解,在这里区分核心线程和普通线程,实际上区分的这么清楚,都是线程
    2.默认的处理方案就是抛出RejectedExecutionException

-- 分析单个线程的线程池的源码 --------------------------------
ExecutorService pool = Executors.newSingleThreadExecutor();
new ThreadPoolExecutor(
    1, -- 核心线程数量 
    1, -- 最大线程数量 
    0L, -- 闲置时间
    TimeUnit.MILLISECONDS, -- 时间单位(毫秒)
    new LinkedBlockingQueue<Runnable>() -- 无界任务队列,可以无限添加任务(内存溢出问题)
)  
-- 分析指定线程的线程池的源码 --------------------------------
ExecutorService pool = Executors.newFixedThreadPool(3);
new ThreadPoolExecutor(
    nThreads, -- 核心线程数量 
    nThreads, -- 最大线程数量 
    0L, -- 闲置时间
    TimeUnit.MILLISECONDS, -- 时间单位(毫秒)
    new LinkedBlockingQueue<Runnable>()-- 无界任务队列,可以无限添加任务(内存溢出问题)
)
-- 创建可缓存线程的线程池 -----------------------------------
new ThreadPoolExecutor(
    0, -- 核心线程数量 
    Integer.MAX_VALUE,-- 最大线程数量 
    60L, -- 闲置时间
    TimeUnit.SECONDS, -- 时间单位()
    new SynchronousQueue<Runnable>() -- 直接提交队列(同步队列):没有容量队列(最大线程数量是21亿多,太大了)

总结:核心线程满载 -> 任务队列 -> 普通线程 -> 拒绝策略

在这里插入图片描述

5.任务队列详解

队列名称详解
LinkedBlockingQueue无界任务队列使用无界任务队列,线程池的任务队列可以无限制的添加新的任务,而线程池创建的最大线程数量就是你corePoolSize设置的数量,也就是说在这种情况下maximumPoolSize这个参数是无效的,哪怕你的任务队列中缓存了很多未执行的任务,当线程池的线程数达到corePoolSize后,就不会再增加了;若后续有新的任务加入,则直接进入队列等待,当使用这种任务队列模式时,一定要注意你任务提交与处理之间的协调与控制,不然会出现队列中的任务由于无法及时处理导致一直增长,直到最后资源耗尽的问题
SynchronousQueue 同步任务队列 直接提交任务队列使用直接提交任务队列,队列没有容量,每执行一个插入操作就会阻塞,需要再执行一个删除操作才会被唤醒,反之每一个删除操作也都要等待对应的插入操作。 任务队列为SynchronousQueue,创建的线程数大于maximumPoolSize时,直接执行了拒绝策略抛出异常。 使用SynchronousQueue队列,提交的任务不会被保存,总是会马上提交执行。如果用于执行任务的线程数量小于maximumPoolSize,则尝试创建新的线程,如果达到maximumPoolSize设置的最大值,则根据你设置的handler执行拒绝策略。因此这种方式你提交的任务不会被缓存起来,而是会被马上执行,在这种情况下,你需要对你程序的并发量有个准确的评估,才能设置合适的maximumPoolSize数量,否则很容易就会执行拒绝策略;
ArrayBlockingQueue有界任务队列使用有界任务队列,若有新的任务需要执行时,线程池会创建新的线程,直到创建的线程数量达到corePoolSize时,则会将新的任务加入到等待队列中。若等待队列已满,即超过ArrayBlockingQueue初始化的容量,则继续创建线程,直到线程数量达到maximumPoolSize设置的最大线程数量,若大于maximumPoolSize,则执行拒绝策略。在这种情况下,线程数量的上限与有界任务队列的状态有直接关系,如果有界队列初始容量较大或者没有达到超负荷的状态,线程数将一直维持在corePoolSize以下,反之当任务队列已满时,则会以maximumPoolSize为最大线程数上限。
PriorityBlockingQueue优先任务队列使用优先任务队列,它其实是一个特殊的无界队列,它其中无论添加了多少个任务,线程池创建的线程数也不会超过corePoolSize的数量,只不过其他队列一般是按照先进先出的规则处理任务,而PriorityBlockingQueue队列可以自定义规则根据任务的优先级顺序先后执行。

对优先队列的使用说明:

除了第一个任务直接创建线程执行外,其他的任务都被放入了优先任务队列,按优先级进行了重新排列执行,且线程池的线程数一直为corePoolSize,也就是只有一个。

5.1 无界队列

理解:这个队列没有上线

继承关系:LinkedBlockingQueue -> AbstractQueue -> AbstractCollection

小结:

1.LinkedBlockingQueue是Collection集合家族的一员

2.Collection集合家族(List、Set、Queue)

3.LinkedBlockingQueue数据结构单向链表

缺点:LinkedBlockingQueue可能造成内存溢出

public class Test01 {
	public static void main(String[] args) throws InterruptedException {
		
		//创建无界队列
		LinkedBlockingQueue<String> queue = new LinkedBlockingQueue<>();
		
		//添加元素
		queue.put("aaa");
		queue.put("bbb");
		queue.put("ccc");
		queue.put("ddd");
		queue.put("eee");
		queue.put("fff");
		
		//删除元素
		queue.remove("ccc");
		
		//遍历队列
		Iterator<String> it = queue.iterator();
		while (it.hasNext()) {
			String string = it.next();
			System.out.println(string);
		}
		
		
	}
}
public class Task implements Runnable,Comparable<Task>{

	private int priority;//优先级别
	
	public Task(int priority) {
		this.priority = priority;
	}

	@Override
	public void run() {
		System.out.println("任务被处理了");
	}

	//当前对象和其他对象做比较,当前优先级大就返回-1,优先级小就返回1,值越小优先级越高
	@Override
	public int compareTo(Task o) {
		return Integer.compare(this.priority, o.priority);
	}

	public int getPriority() {
		return priority;
	}
}
5.2 有界队列

继承关系:ArrayBlockingQueue -> AbstractQueue -> AbstractCollection

小结:

1.ArrayBlockingQueue数据结构一维数组

缺点:不能有效控制项目中的线程目数

public class Test02 {
	public static void main(String[] args) throws InterruptedException {
		
		//创建有界队列
		ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(6);
		
		//添加元素
		queue.put("aaa");
		queue.put("bbb");
		queue.put("ccc");
		queue.put("ddd");
		queue.put("eee");
		queue.put("fff");
		
		//删除元素
		queue.remove("ccc");
		
		//遍历队列
		Iterator<String> it = queue.iterator();
		while (it.hasNext()) {
			String string = it.next();
			System.out.println(string);
		}
				
	}
}
5.3 优先队列

继承关系:ArrayBlockingQueue -> AbstractQueue -> AbstractCollection

ArrayBlockingQueue数据结构一维数组

public class Test03 {
	public static void main(String[] args) throws InterruptedException {
		
		//创建优先队列
		PriorityBlockingQueue<Task> queue = new PriorityBlockingQueue<>();//无参构造底层使用的是元素的内置比较器
		
		//添加元素
		queue.add(new Task(3));
		queue.add(new Task(1));
		queue.add(new Task(4));
		queue.add(new Task(2));
				
		//遍历取出队列
		while(!queue.isEmpty()){
			//删除第一元素
			Task poll = queue.poll();
			System.out.println(poll.getPriority());
		}		
		
	}
}

6.拒绝策略

ThreadPoolExecutor自带的拒绝策略有四种,都实现了RejectedExecutionHandler接口

比如:new ThreadPoolExecutor.AbortPolicy()

拒绝策略解释
AbortPolicy当有任务添加到线程池被拒绝时,会抛出RejectedExecutionException异常 线程池默认的拒绝策略。必须处理好抛出的异常,否则会打断当前的执行流程,影响后续的任务执行。
DiscardPolicy当有任务添加到线程池被拒绝时,直接丢弃,其他啥都没有
CallerRunsPolicy当有任务添加到线程池被拒绝时,线程池会将被拒绝的任务添加到线程池正在运行的线程中去运行。 一一般并发比较小,性能要求不高,不允许失败。但是,由于调用者自己运行任务,如果任务提交速度过快,可能导致程序阻塞,性能效率上必然的损失较大
DiscardOledestPolicy当有任务添加到线程池被拒绝时,线程池会丢弃阻塞队列中末尾的任务(最老的任务),然后将被拒绝的任务添加到末尾。 如果项目中有允许丢失任务的需求,可以使用
6.1 自定义拒绝策略(实现RejectedExecutionHandler接口)
public class Test {
	
	public static void main(String[] args) {
		
		ThreadPoolExecutor pool = new ThreadPoolExecutor(1, 1, 1000, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(1), Executors.defaultThreadFactory(), new RejectedExecutionHandler() {
			@Override
			public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
				System.out.println(r.toString()+"执行了拒绝策略");
			}
		});
		
		for (int i = 1; i <= 10; i++) {
			pool.execute(new Task());
		}
		
		pool.shutdown();
	}
}
class Task implements Runnable{
	
	@Override
	public void run() {
		try {
			Thread.sleep(1000);
			System.out.println(Thread.currentThread().getName());
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

7.自定义线程池原因(重要)

为什么不使用java自带的线程池?而去自定义线程池?

在《阿里巴巴java开发手册》中指出了线程资源必须通过线程池提供,不允许在应用中自行显示的创建线程,这样一方面使线程的创建更加规范,可以合理控制开辟线程的数量;另一方面线程的细节管理交给线程池处理,优化了资源的开销。而线程池不允许使用Executors去创建,而要通过ThreadPoolExecutor方式,这一方面是由于jdk中Executor框架虽然提供了如newFixedThreadPool()、newSingleThreadExecutor()、newCachedThreadPool()等创建线程池的方法,但都有其局限性,不够灵活,而且有资源耗尽的风险(OOM - Out Of Memory )。

一般我们创建线程池时,为防止资源被耗尽,任务队列都会选择创建有界任务队列,但这种模式下如果出现任务队列已满且线程池创建的线程数达到你设置的最大线程数时,这时就需要你指定ThreadPoolExecutor的RejectedExecutionHandler参数即合理的拒绝策略,来处理线程池"超载"的情况

7.1 自定义线程池

七大属性要记住

前提学习线程池中如何创建线程:

线程池中线程就是通过ThreadPoolExecutor中的ThreadFactory,线程工厂创建的。那么通过自定义ThreadFactory,可以按需要对线程池中创建的线程进行一些特殊的设置,如命名、优先级等

public class Test01 {
	public static void main(String[] args) {
		
		ThreadPoolExecutor pool = new ThreadPoolExecutor(
				5, //核心线程数
				20, //最大线程数
				60, //闲置时间
				TimeUnit.SECONDS, //时间单位
				new ArrayBlockingQueue<>(30), //任务队列 -- 有界队列
				new ThreadFactory() {//自定义线程工厂
					private int num = 1;
					@Override
					public Thread newThread(Runnable r) {
						Thread t = new Thread(r);
						t.setName("自定义线程池中的线程"+num);
						t.setPriority(Thread.MAX_PRIORITY);
						num++;
						return t;
					}
				}, 
				new RejectedExecutionHandler() {//自定义拒绝策略
					@Override
					public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
						System.out.println("任务被拒绝了~~~");
					}
				}
			);
		
		for (int i = 1; i <= 100; i++) {
			Task task = new Task(i);
			pool.execute(task);
		}
		
		
		pool.shutdown();
		
	}
}
public class Task implements Runnable{

	private int num;
	
	public Task(int num) {
		this.num = num;
	}

	@Override
	public void run() {
		System.out.println(Thread.currentThread().getName() + "处理了第" + num + "个任务");
	}

}

7.2 自己写一个线程池

自定义线程池类

//自己写的线程池
public class FastThreadPool extends ThreadPoolExecutor{
	
	public FastThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
			BlockingQueue<Runnable> workQueue) {
		super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, new FastThreadFactory(), new FastRejectedExecutionHandler());
	}

	public FastThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
			BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
		super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
	}//七大属性记住,重要!!!
	
	@Override
	protected void beforeExecute(Thread t, Runnable r) {
		System.out.println("线程执行任务之前调用 -- " + r);
	}
	
	@Override
	protected void afterExecute(Runnable r, Throwable t) {
		System.out.println("线程执行任务之后调用 -- " + r);
	}
	
	@Override
	protected void terminated() {
		System.out.println("线程池关闭时调用");
	}
	
	//自定义线程工厂(实现ThreadFactory接口)
	private static class FastThreadFactory implements ThreadFactory{
		private int num;//线程编号
		@Override
		public Thread newThread(Runnable r) {
			Thread t = new Thread(r);
			t.setName("自定义线程池中的线程"+num);
			t.setPriority(Thread.MAX_PRIORITY);
			num++;
			return t;
		}
	}
	
	
	//自定义拒绝策略(实现RejectedExecutionHandler)
	private static class FastRejectedExecutionHandler implements RejectedExecutionHandler{
		@Override
		public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
			System.out.println("任务被拒绝了~~~");
		}
	}

}

任务类

public class Task implements Runnable{

	private int num;
	
	public Task(int num) {
		this.num = num;
	}

	@Override
	public void run() {
		System.out.println(Thread.currentThread().getName() + "处理了第" + num + "个任务");
	}

	@Override
	public String toString() {
		return "任务" + num;
	}
}

测试类

public class Test01 {
	public static void main(String[] args) {
		
		FastThreadPool pool = new FastThreadPool(5, 20, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(30));
		
		for (int i = 1; i <= 100; i++) {
			Task task = new Task(i);
			pool.execute(task);
		}
		
		//设置为true后,闲置时间一旦到达,核心线程也会被销毁
		//经验:我们一般不会回收核心线程,因为设置回收后线程池中的线程有可能为0,这样就没有线程复用率的说法了
		//pool.allowCoreThreadTimeOut(true);
		
		pool.shutdown();
		
	}
}

面试题:线程池中核心线程会被回收吗?

当线程池调用了allowCoreThreadTimeOut(true);时,核心线程会被回收

但是一般不会调用该方法,也就是说,项目中线程池中的核心线程不要设计被回收,因为如果线程池中线程全部都回收了,就没有线程复用率这个说法

核心线程宁可闲置也不回收

7.3 ThreadPoolExecutor扩展

ThreadPoolExecutor扩展主要是围绕beforeExecute()、afterExecute()和terminated()三个方法的重写, 通过这三个方法我们可以监控每个任务的开始和结束时间,或者其他一些功能。下面我们可以通过代码实现一下

方法解释
beforeExecute线程池中任务运行前执行
afterExecute线程池中任务运行完毕后执行
terminated线程池退出后执行

上面自己写的自定义线程池里有相关使用

8.线程池线程数量

实际工作中使用 sysbench多线程性能测试工具 (重要),因为七大属性中的核心线程数,最大线程数,闲置时间不能随便乱写一个数字上去,而是要进行相关测试才能得出数据

9.带有返回值的任务类(实现 Callable<>接口)

注意:带有返回值的任务类和线程池一起使用

需求:计算任务,一个包含了2万个整数的数组,分拆了多个线程来进行并行计算,最后汇总出计算的结果。

public class Test01 {
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		
		//创建数组
		int[] arr = new int[20000];
		
		//初始化数组数据 -- {1,2,3,....,20000}
		for (int i = 0; i < arr.length; i++) {
			arr[i] = i+1;
		}
		
		//创建线程池
		FastThreadPool pool = new FastThreadPool(4, 4, 0, TimeUnit.SECONDS, new ArrayBlockingQueue<>(5));
		
		//创建任务
		Task task1 = new Task(arr, 0, 5000,1);
		Task task2 = new Task(arr, 5000, 10000,2);
		Task task3 = new Task(arr, 10000, 15000,3);
		Task task4 = new Task(arr, 15000, 20000,4);
		
		//提交任务,任务完成后会返回Future对象,Future对象里存储了任务的返回值数据
		Future<Integer> future1 = pool.submit(task1);
		Future<Integer> future2 = pool.submit(task2);
		Future<Integer> future3 = pool.submit(task3);
		Future<Integer> future4 = pool.submit(task4);
		
		//合并任务返回值
		System.out.println(future1.get() + future2.get() + future3.get() + future4.get());
		
		
		pool.shutdown();
	}
}

带返回值的任务类

public class Task implements Callable<Integer>{
	
	private int[] arr;
	private int startIndex;
	private int endIndex;
	
	private int num;
	
	public Task(int[] arr, int startIndex, int endIndex,int num) {
		this.arr = arr;
		this.startIndex = startIndex;
		this.endIndex = endIndex;
		this.num = num;
	}

	//理解:线程抢到CPU资源后,才会调用call方法,call方法相当于Runnable接口中的run方法
	@Override
	public Integer call() throws Exception {
		
		int sum = 0;
		for (int i = startIndex; i < endIndex; i++) {
			sum += arr[i];
			System.out.println("任务" + num);
		}
		return sum;
	}

}

总结

1.Java自带的线程池
单个线程的线程池
指定线程个数的线程池
可缓存的线程池
延迟任务的线程池

2.线程池的7大参数
核心线程数
最大线程数
任务队列(有界、无界、同步、优先队列)
拒绝策略
闲置时间
时间单位
线程工厂

3.线程池的调用步骤(核心线程、任务队列、普通线程、拒绝策略)

4.任务队列及底层原理(有界、无界、同步、优先队列)

5.自定义线程池
自定义线程工厂
自定义拒绝策略

6.带有返回值的任务类 --Callable

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

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

相关文章

【tensorflow框架神经网络实现鸢尾花分类】

文章目录 1、数据获取2、数据集构建3、模型的训练验证可视化训练过程 1、数据获取 从sklearn中获取鸢尾花数据&#xff0c;并合并处理 from sklearn.datasets import load_iris import pandas as pdx_data load_iris().data y_data load_iris().targetx_data pd.DataFrame…

Flask学习(六):蓝图(Blueprint)

蓝图&#xff08;Blueprint&#xff09;&#xff1a;将各个业务进行区分&#xff0c;然后每一个业务单元可以独立维护&#xff0c;Blueprint可以单独具有自己的模板、静态文件或者其它的通用操作方法&#xff0c;它并不是必须要实现应用的视图和函数的。 Demo目录结构&#xf…

八大技术趋势案例(人工智能物联网)

科技巨变,未来已来,八大技术趋势引领数字化时代。信息技术的迅猛发展,深刻改变了我们的生活、工作和生产方式。人工智能、物联网、云计算、大数据、虚拟现实、增强现实、区块链、量子计算等新兴技术在各行各业得到广泛应用,为各个领域带来了新的活力和变革。 为了更好地了解…

利用Java代码混淆技术提升应用程序抗逆向工程能力

摘要 本文探讨了代码混淆在保护Java代码安全性和知识产权方面的重要意义。通过混淆技术&#xff0c;可以有效防止代码被反编译、逆向工程或恶意篡改&#xff0c;提高代码的安全性。常见的Java代码混淆工具如IPAGuard、Allatori、DashO、Zelix KlassMaster和yGuard等&#xff0…

Python人工智能:气象数据可视化的新工具

Python是功能强大、免费、开源&#xff0c;实现面向对象的编程语言&#xff0c;在数据处理、科学计算、数学建模、数据挖掘和数据可视化方面具备优异的性能&#xff0c;这些优势使得Python在气象、海洋、地理、气候、水文和生态等地学领域的科研和工程项目中得到广泛应用。可以…

物联网实战--入门篇之(一)物联网概述

目录 一、前言 二、知识梳理 三、项目体验 四、项目分解 一、前言 近几年很多学校开设了物联网专业&#xff0c;但是确却地讲&#xff0c;物联网属于一个领域&#xff0c;包含了很多的专业或者说技能树&#xff0c;例如计算机、电子设计、传感器、单片机、网…

葵花卫星影像应用场景及数据获取

一、卫星参数 葵花卫星是由中国航天科技集团公司研制的一颗光学遥感卫星&#xff0c;代号CAS-03。该卫星于2016年11月9日成功发射&#xff0c;位于地球同步轨道&#xff0c;轨道高度约为35786公里&#xff0c;倾角为0。卫星设计寿命为5年&#xff0c;搭载了高分辨率光学相机和多…

Oracle存数字精度问题number、binary_double、binary_float类型

--表1 score是number(10,5)类型 create table TEST1 (score number(10,5) ); --表2 score是binary_double类型 create table TEST2 (score binary_double ); --表3 score是binary_float类型 create table TEST3 (score binary_float );实验一&#xff1a;分别往三张表插入 小数…

抖音视频关键词无水印下载软件|手机网页视频批量提取工具

全新视频关键词无水印下载软件&#xff0c;助您快速获取所需视频&#xff01; 随着时代的发展&#xff0c;视频内容已成为人们获取信息和娱乐的重要途径。为了方便用户获取所需视频&#xff0c;推出了一款功能强大的视频关键词无水印下载软件。该软件主要功能包括关键词批量提取…

【话题】AI大模型学习:理论、技术与应用探索

大家好&#xff0c;我是全栈小5&#xff0c;欢迎阅读小5的系列文章&#xff0c;这是《话题》系列文章 目录 背景1. AI大模型学习的基础理论1.1 机器学习1.2 深度学习 2. AI大模型学习的技术要点2.1 模型结构设计2.2 算法优化2.3 大规模数据处理 3. AI大模型学习的应用场景3.1 自…

网络爬虫框架Scrapy的入门使用

Scrapy的入门使用 Scrapy概述引擎&#xff08;Engine&#xff09;调度器&#xff08;Scheduler&#xff09;下载器&#xff08;Downloader&#xff09;SpiderItem Pipeline 基本使用安装scrapy创建项目定义Item数据模型对象创建爬虫(Spider)管道pipeline来保存数据启动爬虫 其他…

Netty核心原理剖析与RPC实践6-10

Netty核心原理剖析与RPC实践6-10 06-粘包拆包问题&#xff1a;如何获取一个完整的网络包 本节课开始我们将学习 Netty 通信过程中的编解码技术。编解码技术这是实现网络通信的基础&#xff0c;让我们可以定义任何满足业务需求的应用层协议。在网络编程中&#xff0c;我们经常…

高风险IP来自哪里:探讨IP地址来源及其风险性质

在网络安全领域&#xff0c;高风险IP地址是指那些可能涉及恶意活动或网络攻击的IP地址。了解这些高风险IP地址的来源可以帮助网络管理员更好地识别和应对潜在的安全威胁。本文将探讨高风险IP地址的来源及其风险性质&#xff0c;并提供一些有效的应对措施。 风险IP查询&#xf…

Sourcetree如何解决冲突和重置

解决冲突&#xff1a;找到冲突的文件然后点恢复&#xff08;其实是丢弃的意思&#xff09; 重置回某个分支节点&#xff1a;

HTTP——Cookie

HTTP——Cookie 什么是Cookie通过Cookie访问网站 我们之前了解了HTTP协议&#xff0c;如果还有小伙伴还不清楚HTTP协议&#xff0c;可以点击这里&#xff1a; https://blog.csdn.net/qq_67693066/article/details/136895597 我们今天来稍微了解一下HTTP里面一个很小的部分&…

Redis中的LRU算法分析

LRU算法 概述 Redis作为缓存使用时&#xff0c;一些场景下要考虑内容的空间消耗问题。Redis会删除过期键以释放空间&#xff0c;过期键的删除策略 有两种: 1.惰性删除:每次从键空间中获取键时&#xff0c;都检查取得的键是否过期&#xff0c;如果过期的话&#xff0c;就删除…

Adobe最近推出了Firefly AI的结构参考以及面向品牌的GenStudio

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

Rust编程(四)PackageCrateModule

这一部分的中文教程/文档都很混乱,翻译也五花八门,所以我建议直接看英文官方文档,对于一些名词不要进行翻译,翻译只会让事情更混乱,本篇从实战和实际需求出发,讲解几个名称的关系。 Module & Crate & Package & Workspace 英文中的意思: Cargo:货物 Crate:…

Apache HBase(二)

目录 一、Apache HBase 1、HBase Shell操作 1.1、DDL创建修改表格 1、创建命名空间和表格 2、查看表格 3、修改表 4、删除表 1.2、DML写入读取数据 1、写入数据 2、读取数据 3、删除数据 2、大数据软件启动 一、Apache HBase 1、HBase Shell操作 先启动HBase。再…

【已修复】iPhone13 Pro 长焦相机水印(黑斑)修复 洗水印

iPhone13 Pro 长焦相机水印&#xff08;黑斑&#xff09;修复 洗水印 问题描述 iPhone13 Pro 后摄3倍相机有黑色斑点&#xff08;水印&#xff09;&#xff0c;如图所示&#xff0c; 后摄相机布局如图所示&#xff0c; 修复过程 拆机过程有风险&#xff0c;没有把握最好不要…