当前位置: 首页 > news >正文

Android相关线程基础

线程基础

进程与线程

进程:可以被看做是程序的实体, 是系统进行资源分配和调度的基本单位.

线程:是操作系统调度的最小单元, 也叫轻量级进程

使用多线程的优点

可以减少程序的响应时间。如果某个操作很耗时, 能够避免陷入长时间的等待, 从而有着更好的交互性.

线程较之进程, 创建和切换的开销更小, 在共享数据方面的效率非常高.更高利用多CPU或多核设备的性能

简化程序结构, 便于理解维护.

线程的状态

  • New
  • Runnable(可运行状态, 不是立即运行, 取决于系统的决定)
  • Blocked(当调用同步方法而未获取锁时会进入阻塞状态)
  • Waiting(暂时不活动, 不运行任何代码, 消耗最少的资源)
  • Timed waiting(与waiting相比, 能在指定的时间自行返回)
  • Terminated(终止状态,已经执行完毕或者异常退出)

阻塞状态是线程因为某些条件不满足(如等待I/O操作或等待获取锁),而暂时停止执行,直至条件得到满足。处于等待的线程不会尝试获取锁,它是在等待其他线程显式地通知或中断,才返回到可运行状态。

创建线程

创建线程的方法一般有三种:

继承Thread类, 重写run方法(本质为实现Runnable接口的一个实例)

1.创建继承Thread类的子类, 并重写run方法(执行体).

2.创建子类的实例

3.调用实例对象的start方法启动线程

实现Runnable接口, 并实现接口的run方法

1.自定义一个实现Runnable接口的类, 实现run方法

2.创建上面的类的对象, 并将其作为参数去创建一个Thread子类的实例.Thread mThread = new Thread(参数)

3.调用Thread.start().

实现Callable接口, 重写call方法

是Executor框架中的功能类, 与Runnable接口的功能类似, 但提供了比Runnable更强大的功能, 主要表现在以下3点:

1.任务结束后提供一个返回值,

2.call方法可以抛出异常

3.运行了Callback后可以得到一个Future对象, 该对象利用Future.get方法监视目标线程调用call方法的情况, 但会一直阻塞直到call方法返回结果

进程中断

线程中断是一种协作机制,它允许一个线程告知另一个线程希望它停止当前正在做的事情。中断是一种软件协议,而不是强迫线程停止的方法。当一个线程中断另一个线程时,被中断的线程并不会立即停止运行,而是由这个线程决定如何响应中断。

理解线程中断的几个关键点如下:

  1. 中断标志位:每个线程都有一个中断状态,它表示线程是否被中断。通过调用线程实例的 interrupt() 方法可以设置这个线程的中断状态。如果线程在执行时没有检查中断状态,则调用 interrupt() 不会立刻停止线程。
  2. 检查中断:线程可以通过调用静态方法 Thread.interrupted() 或实例方法 isInterrupted() 来检查它自己是否被中断。Thread.interrupted() 会清除当前线程的中断状态,而 isInterrupted() 则不会。
  3. 响应中断:当线程检测到中断请求时,可以通过多种方式来响应:
    1. 完全忽略中断请求,这通常不是好的做法,因为这会让发送中断的线程很难控制这个线程。
    2. 清理资源,停止当前任务并优雅退出。
    3. 抛出 InterruptedException,这通常发生在线程阻塞操作(如 sleep(), wait(), join())中被中断时。在抛出 InterruptedException 之前,JVM会先将这个线程的中断状态清除,然后抛出异常。
  4. 不应该忽视中断:一个设计良好的线程任务在检测到中断时,应当尽快清理资源,保存状态,并且合理地结束执行,以便程序整体可以正确反应中断请求,如停止或重启任务。
class Task implements Runnable {public void run() {while (!Thread.currentThread().isInterrupted()) {// 在这里执行任务工作try {// 假设此处有可能阻塞的操作Thread.sleep(1000);} catch (InterruptedException e) {// 当线程在sleep时被中断,会进入这个catch块System.out.println("Thread was interrupted, safely stopping.");break; // 退出循环,结束线程的执行}}// 清理资源和状态}
}public class InterruptExample {public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(new Task());thread.start();// 给线程一些时间来启动并执行任务Thread.sleep(3000);// 发出中断请求thread.interrupt();}
}

线程同步

  • 同步 Java中的同步指的是通过人为的控制和调度,保证共享资源多线程访问成为线程安全,来保证结果的准确。

ReentrantLock(重入锁)

ReentrantLock定义了一个单独的布尔值,标记锁是否被任何线程锁定。如果是,则持有锁的线程可以再次获得,但必须释放它同样多的次数才能解锁。

class SharedObject {private ReentrantLock lock = new ReentrantLock();void sharedMethod() {lock.lock();  // 在访问临界区之前获取锁try {// 临界区:只有一个线程在同一时间可以访问// 像这样的代码...} finally {lock.unlock();  //总是在finally块中释放锁}}
}

关于sleep与wait的不同点:

  • sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。
  • wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用
  • sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常
  • sleep是线程类(Thread)的方法,导致此线程暂停执行指定时间,给执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复。调用sleep不会释放对象锁。
  • wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。

重入锁(ReentrantLock)与条件对象

支持重进入的锁, 表示该锁支持一个线程对锁本身的重复加锁(其他的线程此时不能对该锁加锁)

加锁的次数要和解锁的次数要相同, 否则其他线程对此锁无法加锁

条件对象condition中提供了condition.await()方法(进入阻塞状态并放弃锁)和condition.signalAll()方法(将该条件对象上等待的线程唤醒, 解除阻塞, 这里说明一下并不是立即激活)

//对重入锁和条件对象的测试
public class HelloWorld {static ReentrantLock lock = new ReentrantLock();static Condition condition = lock.newCondition();public static void main(String[] args) throws InterruptedException {lock.lock();new Thread(new SignalThread()).start();System.out.println("主线程等待通知");try {System.out.println("主线程的前try");condition.await(); //释放锁, 等唤醒后需要重新获取锁System.out.println("主线程的后try");} finally {System.out.println("主线程的final");lock.unlock();}System.out.println("主线程恢复运行");}static class SignalThread implements Runnable {@Overridepublic void run() {lock.lock();try {System.out.println("子线程的前try");condition.signal();System.out.println("子线程通知");} finally {System.out.println("子线程的final");lock.unlock();}}}
}

同步方法

使用synchronized修饰的方法叫做同步方法,java中每一个对象都有一个锁,使用synchronized声明一个方法,那么对象的锁将会保护一整个方法,使用同步方法是为了防止多个线程在更新相同的资源时导致数据出错。

  • 实例方法锁定当前对象的实例(使用this作为锁对象)。
  • 静态方法锁定当前对象所属的Class对象(使用class对象作为锁对象)

同步代码块

使用synchronized关键字加上一个锁对象来创建一个执行区域,这个区域内的代码在同一时刻只能被一个线程执行。

synchronized(锁对象) {// 需要同步的代码
}

锁对象的选择

  • 使用特定对象作为锁对象:通常是共享资源或者与共享资源紧密相关的对象。
  • 使用当前实例this作为锁对象:适用于多个线程访问某个对象实例的同步问题。
  • 使用类对象ClassName.class作为锁对象:适用于静态方法或者静态资源的同步。

volatile

volatile是Java提供的一种特殊的变量类型修饰符,主要用于确保多线程环境中共享变量的可见性(线程A在修改了本地内存的值后,可能还没来得及同步回主内存,线程B就已经读取了该变量,这时候线程B读取到的就是旧的值,也就实现了线程A修改的值对线程B不可见,这就是所谓的可见性问题)和顺序性。

  1. java内存模型

Java中的堆内存用来存储对象实例,堆内存是被所有线程共享的运行时内存区域,因此,它存在内存可见性的问题。而局部变量、方法定义的参数则不会在线程之间共享,它们不会有内存可见性问题,也不受内存模型的影响。Java内存模型定义了线程和主存之间的抽象关系:线程之间的共享变量存储在主存中,每个线程都有一个私有的本地内存,本地内存中存储了该线程共享变量的副本。需要注意的是本地内存是Java内存模型的一个抽象概念,Java内存模型控制线程之间的通信,它决定一个线程对主存共享变量的写入何时对另一个线程可见。Java内存模型的抽象示意图如图所示。
在这里插入图片描述

线程A与线程B之间若要通信的话,则必须要经历下面两个步骤:

(1)线程A把线程A本地内存中更新过的共享变量刷新到主存中去。

(2)线程B到主存中去读取线程A之前已更新过的共享变量。由此可见,如果我们执行下面的语句:

int i=3;

执行线程必须先在自己的工作线程中对变量i所在的缓存行进行赋值操作,然后再写入主存中,而不是直接将数值3写入主存中。

  1. 原子性,可见性和有序性

  2. 原子性

    1. 原子性指的是一个或多个操作要么全部执行且在执行过程中不会被任何因素打断,要么就全部都不执行。在Java中,原子性操作通常是指不可分割的操作,比如对基本数据类型(除了long和double之外)的读取和赋值操作。例如,当一个线程正在执行一个原子操作时,其他线程不能中断它,直到该操作完全完成。synchronized可以帮助确保方法或者代码块在执行时不会被其他线程干扰,保证了操作的原子性。
  3. 可见性 可见性是指一个线程对共享变量的修改能够及时地被其他线程见到。缺乏可见性时,一个线程可能无法看到另一个线程对共享变量所做的修改。在Java中,可见性问题通常是通过使用volatile关键字或者同步机制(如synchronizedLock)来解决。volatile保证了一个线程修改的变量值对其他线程来说立刻变得可见,而synchronized保证了一个线程在访问共享资源时,先清空工作内存,然后从主内存加载资源。

  4. 有序性 有序性是指程序执行的顺序按照代码的先后顺序执行。由于编译器优化,处理器可能会对指令进行重排序,使得程序执行的顺序与代码书写的顺序不同。在多线程环境中,这可能导致严重问题。为了预防指令重排序的问题,可以使用volatile关键字或者synchronized关键字。它们都可以提供一定程度的有序性保证。volatile变量规则可以确保对volatile变量的写操作不会和之前的读写操作重排序;相似地,synchronized可以确保获取和释放monitor会形成一个内存屏障,避免指令重排序。

  5. volatile关键字

前面提到的三个特性volatile关键字可以保证可见性(一个线程修改了volatile变量的值,新值对于其他线程来说是立即可见的)以及有序性(当一个变量声明为volatile后,会有一个“屏障”限制重排序,确保在volatile变量写操作之前的所有操作都不会被编译器重排序到写操作之后),但是它是不保证原子性的,如果由原子性相关需求还是考虑synchronized

使用它有俩个条件

  • , 即变量真正独立于(不能使用)其他变量和自己以前的值, 即不能自增, 自减, 因为不能保证原子性.
  • 没有包含在其他变量的不变式中(两个线程都会通过用于保护不变式的检查导致错误), 比如 [0, 10], 一个线程修改最大值为5, 同时另一个线程修改最小值为6, 此时区间就变成了[6, 5]. 很明显就错了

使用场景

1.状态标志, 使用volatile修饰Boolean类型的变量, 不依赖于程序内的任何其他状态 2.双重检查锁定模式(DCL)

阻塞队列

阻塞队列简介

阻塞队列常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消费者是从队列里拿元素的线程。阻塞队列就是生产者存放元素的容器,而消费者也只从容器里拿元素。一个线程可以从队列中获取元素,如果队列为空,那么线程会阻塞直到队列中有元素;同样地,一个线程可以向队列中插入元素,如果队列已满,那么该线程会阻塞直到队列中有空间。

  1. 阻塞队列就是生产者存放元素的容器, 常见的阻塞场景: 1.队列没有数据, 消费者端的所有线程都会自动挂起(阻塞), 直到有数据放入队列,线程被唤醒 2.队列中数据填满了, 生产者端的所有线程都会自动挂起, 直到队列中有空的位置, 线程被唤醒

支持这两种阻塞场景的队列叫做阻塞队列

BlockingQueue的核心方法:

放入数据

  1. offer(anObject): 如果阻塞队列可以容纳, 返回true, 否则返回false, 并且将anObject放入阻塞队列. (本方法不会阻塞当前执行方法的线程)
  2. offer(E, long, TimeUnit): 在指定时间内如果不能往队列中添加, 则返回false
  3. put(anObject): 如果阻塞队列没有空间, 则调用此方法的线程被阻断, 直到有空间再继续.

获取数据

  1. poll(time): 在等待的时间内都不能取到排在队列首位的对象就返回null.
  2. poll(long, TimeUnit): 在指定时间内不能取出数据, 返回false
  3. take(): 为空则阻断
  4. drainTo(): 取出所有的可用数据对象(还可以指定个数), 可以提高数据获取效率, 无需多次分批加锁. 释放锁
public class ProducerConsumerExample {public static void main(String[] args) {BlockingQueue<String> queue = new LinkedBlockingQueue<>(10);// 生产者线程Thread producer = new Thread(() -> {try {queue.put("Product");} catch (InterruptedException e) {Thread.currentThread().interrupt();e.printStackTrace();}});// 消费者线程Thread consumer = new Thread(() -> {try {String product = queue.take();System.out.println("Consumed: " + product);} catch (InterruptedException e) {e.printStackTrace();}});producer.start();consumer.start();}
}

java中的阻塞队列

  • ArrayBlockingQueue: 由数据结构组成的有界阻塞队列
  • LinkedBlockingQueue: 由链表结构组成的有界阻塞队列
  • PriorityBlockingQueue: 支持优先级排序的无界阻塞队列
  • DelayQueue: 使用优先级队列实现的无界阻塞队列
  • SynchronousQueue: 不储存元素的阻塞队列
  • LinkedTransferQueue: 由链表结构组成的无界阻塞队列
  • LinkedBlockingQueue: 由链表结构组成的双向阻塞队列

线程池

ThreadPoolExecutor

ThreadPoolExecutor的构造方法

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactoryRejectedExecutionHandler handler)

corePoolSize

线程池的核心线程数,默认情况下,核心线程会在线程池中一直存活,即使它们处于闲置状态。如果将ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true,那么闲置的核心线程在等待新任务到来时会有超时策略,这个时间间隔由keepAliveTime所指定,当等待时间超过keepAliveTime所指定的时长后,核心线程就会被终止。

maximumPoolSize

线程池所能容纳的最大线程数,当活动线程数达到这个数值后,后续的新任务将会被阻塞。

keepAliveTime

非核心线程闲置时的超时时长,超过这个时长,非核心线程就会被回收。当ThreadPool-Executor的allowCoreThreadTimeOut属性设置为true时,keepAliveTime同样会作用于核心线程。

unit

用于指定keepAliveTime参数的时间单位,这是一个枚举,常用的有TimeUnit. MILLISECONDS(毫秒)、TimeUnit.SECONDS(秒)以及TimeUnit.MINUTES(分钟)等。

workQueue

线程池中的任务队列,通过线程池的execute方法提交的Runnable对象会存储在这个参数中。

threadFactory

线程工厂,为线程池提供创建新线程的功能。ThreadFactory是一个接口,它只有一个方法:Thread newThread(Runnable r)。

RejectedExecutionHandler

饱和策略,当任务队列和线程池都满了的时候采取的应对策略,有四种拒绝策略

  1. AbortPolicy (默认): 这种策略会直接抛出RejectedExecutionException异常,中止任务的执行。这是默认的策略,如果没有设置拒绝策略,线程池会采用这种策略。
  2. CallerRunsPolicy: 这种策略不会抛弃任务,也不会抛出异常,而是将任务回退给调用者线程来直接执行。这意味着如果线程池无法处理任务,那么执行任务的将会是提交任务的线程本身。
  3. DiscardPolicy: 这种策略将静默地忽略无法处理的任务,不抛出异常也不执行任务。如果应用程序可以容忍任务丢失,这种策略会很有用。
  4. DiscardOldestPolicy: 这个策略将丢弃队列中最老的一个任务,然后尝试提交当前的任务(也就是再次提交,而不保证一定会执行)。这样做至少可以保证新提交的任务被执行。

ThreadPoolExecutor执行任务时大致遵循如下规则:

  1. 应用程序向线程池提交一个任务。
  2. 如果当前活跃的线程数小于核心线程数,线程池会创建一个新的工作者线程来执行提交的任务。
  3. 如果核心线程都在忙,新任务就会被放入工作队列中等待。
  4. 如果工作队列已满,而且活跃的线程数小于最大线程数限制,线程池会创建额外的工作者线程来处理队列中的任务。
  5. 如果达到了线程数上限,新提交的任务将根据饱和策略来处理。
    在这里插入图片描述

(1)提交任务后,线程池先判断线程数是否达到了核心线程数(corePoolSize)。如果未达到核心线程数,则创建核心线程处理任务;否则,就执行下一步操作。

(2)接着线程池判断任务队列是否满了。如果没满,则将任务添加到任务队列中;否则,就执行下一步操作。

(3)这时,因为任务队列满了,所以线程池就判断线程数是否达到了最大线程数。如果未达到最大线程数,则创建非核心线程处理任务;否则,就执行饱和策略,默认会抛出RejectedExecutionException异常。

上面介绍了线程池的处理流程,但还不是很直观。下面结合图4-7,我们就能更好地了解线程池的原理了。

在这里插入图片描述

总结,先核心线程,条件不允许,任务队列,条件不允许,非核心线程,条件不允许,饱和策略(这个过程中,核心线程会从任务队列中提取任务)

线程池种类

  1. FixedThreadPool

通过Executors的newFixedThreadPool方法来创建。它是一种线程数量固定的线程池,当线程处于空闲状态时,它们并不会被回收,除非线程池被关闭了。当所有的线程都处于活动状态时,新任务都会处于等待状态,直到有线程空闲出来。由于FixedThreadPool只有核心线程并且这些核心线程不会被回收,这意味着它能够更加快速地响应外界的请求。newFixedThreadPool方法的实现如下,可以发现FixedThreadPool中只有核心线程并且这些核心线程没有超时机制,另外任务队列也是没有大小限制的。

public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads,nThreads, 0L,TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); 
}
  1. CachedThreadPool

通过Executors的newCachedThreadPool方法来创建。它是一种线程数量不定的线程池,它只有非核心线程,并且其最大线程数为Integer.MAX_VALUE。由于Integer.MAX_VALUE是一个很大的数,实际上就相当于最大线程数可以任意大。当线程池中的线程都处于活动状态时,线程池会创建新的线程来处理新任务,否则就会利用空闲的线程来处理新任务。线程池中的空闲线程都有超时机制,这个超时时长为60秒,超过60秒闲置线程就会被回收。和FixedThreadPool不同的是,CachedThreadPool的任务队列其实相当于一个空集合,这将导致任何任务都会立即被执行,因为在这种场景下SynchronousQueue是无法插入任务的。SynchronousQueue是一个非常特殊的队列,在很多情况下可以把它简单理解为一个无法存储元素的队列,由于它在实际中较少使用,这里就不深入探讨它了。从CachedThreadPool的特性来看,这类线程池比较适合执行大量的耗时较少的任务。当整个线程池都处于闲置状态时,线程池中的线程都会超时而被停止,这个时候CachedThreadPool之中实际上是没有任何线程的,它几乎是不占用任何系统资源的。newCachedThreadPool方法的实现如下所示

public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0,Integer.MAX_VALUE,60L,TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); 
}
  1. ScheduledThreadPool

通过Executors的newScheduledThreadPool方法来创建。它的核心线程数量是固定的,而非核心线程数是没有限制的,并且当非核心线程闲置时会被立即回收。ScheduledThreadPool这类线程池主要用于执行定时任务和具有固定周期的重复任务,newScheduledThreadPool方法的实现如下所示。

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { return new ScheduledThreadPoolExecutor(corePoolSize); 
} 
public ScheduledThreadPoolExecutor(int corePoolSize) { super(corePoolSize,Integer.MAX_VALUE,0,NANOSECONDS, new DelayedWorkQueue()); 
}
  1. SingleThreadExecutor

通过Executors的newSingleThreadExecutor方法来创建。这类线程池内部只有一个核心线程,它确保所有的任务都在同一个线程中按顺序执行。SingleThreadExecutor的意义在于统一所有的外界任务到一个线程中,这使得在这些任务之间不需要处理线程同步的问题。newSingleThreadExecutor方法的实现如下所示。

public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1,1, 0L,TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); 
}

AsyncTask

这部分内容在前一本书中处理过,这里总结一下他的过程

  1. 定义AsyncTask:首先需要继承AsyncTask类,并定义相关的泛型参数以及重写必要的方法,例如doInBackground
  2. 执行execute方法:在UI线程启动AsyncTask时,通过执行execute(Params...)方法开始异步操作。该方法内部调用了executeOnExecutor方法,并传递sDefaultExecutor作为执行器,默认情况下,这是一个串行执行的线程池。
  3. 状态检查与预操作:AsyncTask首次执行前,会检查其状态。如果任务尚未执行(PENDING状态),那么设置其状态为RUNNING,并调用onPreExecute()方法来进行初始化操作。
  4. 任务封装与线程池调用:传递给execute方法的参数会被封装成FutureTask对象,提交给Executor(例如SerialExecutor或THREAD_POOL_EXECUTOR)来执行。
  5. 执行doInBackground方法:在工作线程中,doInBackground(Params...)方法被执行,该方法负责处理后台计算,其返回值会作为结果传递给onPostExecute方法。
  6. 结果的消息传递:doInBackground方法的结果会被封装在Message中,并通过Handler发送到主线程处理。这个Handler在AsyncTask类加载时已初始化在主线程,以确保消息能够被主线程接收和处理。
  7. 结果处理:在UI线程中,Handler会接收到包含结果的Message,并根据消息的类型调用onPostExecuteonProgressUpdate方法。
  8. 任务完成:在onPostExecute方法中进行UI更新等操作。如果任务在执行中被取消,则onCancelled方法会被调用。

相关文章:

Android相关线程基础

线程基础 进程与线程 进程:可以被看做是程序的实体, 是系统进行资源分配和调度的基本单位. 线程:是操作系统调度的最小单元, 也叫轻量级进程 使用多线程的优点 可以减少程序的响应时间。如果某个操作很耗时, 能够避免陷入长时间的等待, 从而有着更好的交互性. 线程较之进…...

uniapp 如何自定义导航栏并自适应机型

如今的移动设备有各种不同的屏幕形状&#xff0c;如刘海屏、水滴屏等。这些异形屏会影响页面的布局&#xff0c;尤其是导航栏和底部栏的显示。通过获取安全区域信息&#xff0c;可以确保页面内容不会被异形屏的特殊区域遮挡。 在设计页面顶部导航栏时&#xff0c;可以根据 saf…...

Java高级Day43-类加载

117.类加载 静态和动态加载 反射机制是java实现动态语言的关键&#xff0c;也就是通过反射实现类动态加载 静态加载&#xff1a;编译时加载相关的类&#xff0c;如果没有则报错&#xff0c;依赖性太强 动态加载&#xff1a;运行时加载需要的类&#xff0c;如果运行时不用该类…...

【LeetCode 算法笔记】155. 最小栈

目录 问题描述单个栈实现双栈实现不开辟额外空间 问题描述 设计一个支持 push &#xff0c;pop &#xff0c;top 操作&#xff0c;并能在常数时间内检索到最小元素的栈。 实现 MinStack 类: MinStack() 初始化堆栈对象。 void push(int val) 将元素val推入堆栈。 void pop()…...

面试题 05.01. 插入

目录 一&#xff1a;题目&#xff1a; 二&#xff1a;代码&#xff1a; 三&#xff1a;结果&#xff1a; 一&#xff1a;题目&#xff1a; 给定两个整型数字 N 与 M&#xff0c;以及表示比特位置的 i 与 j&#xff08;i < j&#xff0c;且从 0 位开始计算&#xff09;。…...

稠密向量检索、稀疏向量检索、BM25检索三者对比

在当今的信息检索领域&#xff0c;随着人工智能和自然语言处理技术的发展&#xff0c;稠密向量检索和稀疏向量检索成为了两种主要的研究方向。稠密向量检索依托于高维空间中的向量表示&#xff0c;能够捕捉文档的深层语义信息&#xff0c;而稀疏向量检索则侧重于关键词的匹配&a…...

UEFI学习笔记(六):EDK II 模块:Libraries,DriversApplication

UEFI学习笔记&#xff08;六&#xff09;&#xff1a;EDK II Modules&#xff1a;Libraries&#xff0c;Application&Drivers 一、模块&#xff08;Modules&#xff09;的概念1、Library模块2、Application模块3、Driver模块4、Application和Driver的区别 二、EDK II 实现U…...

详解 Pandas 的透视表函数

Pandas 的透视表函数主要为 pivot() 和 pivot_table()&#xff0c;主要的功能为对 DataFrame 的行和列进行重新组合来重塑数据。 一、pivot 函数 pivot 函数只能对数据进行重塑&#xff0c;不能进行聚合 1. 数据准备 import pandas as pddf1 pd.DataFrame({department_id: […...

基于python+django+vue的农业管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于pythondjangovueMySQL的农…...

动态内存管理之malloc,free,calloc和realloc函数

Hello&#xff0c;各位小伙伴们&#xff0c;小编在这里祝福各位中秋佳节快乐呀&#xff0c;今天让我们来学习一下动态内存管理吧&#xff01; 引言 像我们之前在开辟一段空间的时候你可能会使用整型变量来申请一块空间&#xff0c;或者使用数组来申请一段连续的空间&#xff…...

Android 13 固定systemUI的状态栏为黑底白字,不能被系统应用或者三方应用修改

目录 一.背景 二.思路 三.代码流程 1.colos.xml自定义颜色 2.设置状态栏的背景颜色 3.对View进行操作 ①.对Clock(状态栏左侧的数字时钟)进行操作 ②.对电池(BatteryMeterView)进行操作 4.锁屏状态栏 5.patch汇总 一.背景 客户需求将状态栏固定成黑底白字,并且不能让系…...

【CTF Reverse】XCTF GFSJ1092 easyEZbaby_app Writeup(Android+逆向工程+Java)

easyEZbaby_app 究极简单的安卓逆向 解法 得到一个 apk 安装包。 用 jadx 打开&#xff0c;搜索文本 flag&#xff0c;加载所有。 flag 是 obj obj2&#xff0c;来自用户的用户名和密码。 Override // android.view.View.OnClickListenerpublic void onClick(View view) {St…...

ubuntu 22.04 ~24.04 如何修改登录背景

ubuntu 22.04 ~24.04 如何修改登录背景 背景&#xff1a;由于22.04 登录gdm的变更&#xff0c;之前的修改登录背景的方案已经无法使用。现在给大家分享新的使用方法&#xff1a; 1&#xff0c;下载如下路径的脚本&#xff1a; https://download.csdn.net/download/xdhyqd/89…...

Andrej Karpathy谈AI未来:自动驾驶、Transformer与人机融合

引言 在人工智能领域&#xff0c;Andrej Karpathy 是一个无法忽视的名字。从他早期在 OpenAI 的工作&#xff0c;到后来担任 Tesla 的 AI 主管&#xff0c;他在自动驾驶、深度学习等方面的贡献广为人知。最近&#xff0c;卡帕西做客了著名的播客节目 No Priors&#xff0c;他在…...

Vue使用query传参Boolean类型,刷新之后转换为String问题

做项目时发现第一次进入页面时传参是正常的Boolean类型&#xff0c;刷新之后变成了String&#xff0c;这是浏览器进行的一次强制转换&#xff1b; vue-router 传参&#xff0c;不管是 params 形式还是query形式传参&#xff0c;在页面刷新后&#xff0c;params 和 query 对象中…...

开源模型应用落地-qwen模型小试-调用Qwen2-VL-7B-Instruct-更清晰地看世界(一)

一、前言 学习Qwen2-VL ,为我们打开了一扇通往先进人工智能技术的大门。让我们能够深入了解当今最前沿的视觉语言模型的工作原理和强大能力。这不仅拓宽了我们的知识视野,更让我们站在科技发展的潮头,紧跟时代的步伐。 Qwen2-VL 具有卓越的图像和视频理解能力,以及多语言支…...

国学盛典 致敬先贤 《老子与道德经》纪录片研讨会在北京善品堂国学馆圆满落幕

9月10日&#xff0c;《老子与道德经》纪录片研讨会在北京善品堂国学馆圆满落幕。中国著名表演艺术家、曾饰演央视86版电视剧《西游记》中“孙悟空”的六小龄童先生与两百余人传统文化传播者、践行者、爱好者齐聚一堂&#xff0c;共同交流。本次会议由中国文化促进会福文化工作委…...

sqlgun新闻管理系统

一&#xff0c;打开主页 1.输入框测试回显点 -1union select 1,2,3# 出现回显点2 2.查看数据库表名 -1union select 1,database(),3# 3.查看表名 -1union select 1,2,group_concat(table_name) from information_schema.tables where table_schemasqlgunnews# 4.查看admin中…...

react hooks--useState

概述 useState 可以使函数组件像类组件一样拥有 state&#xff0c;也就说明函数组件可以通过 useState 改变 UI 视图。那么 useState 到底应该如何使用&#xff0c;底层又是怎么运作的呢&#xff0c;首先一起看一下 useState 。 问题&#xff1a;Hook 是什么? 一个 Hook 就是…...

C/C++:优选算法(持续更新~~)

一、双指针 1.1移动零 链接&#xff1a;283. 移动零 - 力扣&#xff08;LeetCode&#xff09; 给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。请注意 &#xff0c;必须在不复制数组的情况下原地对数组进行操…...

手游刚开服就被攻击怎么办?如何防御DDoS?

开服初期是手游最脆弱的阶段&#xff0c;极易成为DDoS攻击的目标。一旦遭遇攻击&#xff0c;可能导致服务器瘫痪、玩家流失&#xff0c;甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案&#xff0c;帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...

突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合

强化学习&#xff08;Reinforcement Learning, RL&#xff09;是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程&#xff0c;然后使用强化学习的Actor-Critic机制&#xff08;中文译作“知行互动”机制&#xff09;&#xff0c;逐步迭代求解…...

简易版抽奖活动的设计技术方案

1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...

【WiFi帧结构】

文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成&#xff1a;MAC头部frame bodyFCS&#xff0c;其中MAC是固定格式的&#xff0c;frame body是可变长度。 MAC头部有frame control&#xff0c;duration&#xff0c;address1&#xff0c;address2&#xff0c;addre…...

从零实现富文本编辑器#5-编辑器选区模型的状态结构表达

先前我们总结了浏览器选区模型的交互策略&#xff0c;并且实现了基本的选区操作&#xff0c;还调研了自绘选区的实现。那么相对的&#xff0c;我们还需要设计编辑器的选区表达&#xff0c;也可以称为模型选区。编辑器中应用变更时的操作范围&#xff0c;就是以模型选区为基准来…...

(二)TensorRT-LLM | 模型导出(v0.20.0rc3)

0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述&#xff0c;后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作&#xff0c;其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...

1688商品列表API与其他数据源的对接思路

将1688商品列表API与其他数据源对接时&#xff0c;需结合业务场景设计数据流转链路&#xff0c;重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点&#xff1a; 一、核心对接场景与目标 商品数据同步 场景&#xff1a;将1688商品信息…...

多模态大语言模型arxiv论文略读(108)

CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题&#xff1a;CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者&#xff1a;Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...

C#学习第29天:表达式树(Expression Trees)

目录 什么是表达式树&#xff1f; 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持&#xff1a; 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...

【无标题】湖北理元理律师事务所:债务优化中的生活保障与法律平衡之道

文/法律实务观察组 在债务重组领域&#xff0c;专业机构的核心价值不仅在于减轻债务数字&#xff0c;更在于帮助债务人在履行义务的同时维持基本生活尊严。湖北理元理律师事务所的服务实践表明&#xff0c;合法债务优化需同步实现三重平衡&#xff1a; 法律刚性&#xff08;债…...