Android多线程开发详解

article/2025/9/15 7:08:58

一、基本概念

1、时间片轮转机制

如果在时间片结束时进程还在运行,则CPU将被剥夺并分配给另一个进程。如果进程在时间片结束前阻塞或结来,则CPU当即进行切换。调度程序所要做的就是维护一张就绪进程列表,当进程用完它的时间片后,它被移到队列的末尾。
每个进程被分配一个时间段,称作它的时间片,即该进程允许运行的时间。
从一个进程切换到另一个进程是需要定时间的,包括保存和装入寄存器值及内存映像,更新各种表格和队列等。假如进程切换,有时称为上下文切换,需要5ms,再假设时间片设为20ms,则在做完20ms有用的工作之后,CPU将花费5ms来进行进程切换。CPU时间的20%被浪费在了管理开销上了。

2、Java里的程序天生就是多线程的

一个Java程序从main()方法开始执行,然后按照既定的代码逻辑执行,看似没有其他线程参与,但实际上Java程序天生就是多线程程序,因为执行main()方法的是一个名称为main的线程。Java程序运行后打印当前线程如下:
[6] Monitor Ctrl-Break //监控Ctrl-Break中断信号的
[5] Attach Listener //内存dump,线程dump,类信息统计,获取系统属性等
[4] Signal Dispatcher // 分发处理发送给JVM信号的线程
[3] Finalizer // 调用对象finalize方法的线程
[2] Reference Handler//清除Reference的线程
[1] main //main线程,用户程序入口

二、线程

1、启动

实现线程的方式:
1、T extends Thread; 然后重写run方法
2、T implements Runnable;然后交给Thread运行
3、T implements Callable;然后交给Thread运行
调用Thread的start方法就可以启动线程了。注意不是调用Thread的run方法,直接调用run方法只是调用实例方法而已。线程的启动是由系统调度的,我们应该调用start方法,这是线程就成新建状态,系统会调用开始线程调用run方法。

2、停止

1)、自然停止

  • run执行完成了
  • 抛出了一个未处理的异常导致线程提前结束

2)、手动停止
暂停、恢复和停止操作对应在线程Thread的API就是suspend()、resume()和stop()。但是这些API是过期的,也就是不建议使用的。不建议使用的原因主要有:stop()方法在终结一个线程时不会保证线程的资源正常释放,通常是没有给予线程完成资源释放工作的机会,因此会导致程序可能工作在不确定状态下。正因为suspend()、resume()和stop()方法带来的副作用,这些方法才被标注为不建议使用的过期方法。
安全的中止则是其他线程通过调用某个线程A的interrupt()方法对其进行中断操作, 中断好比其他线程对该线程打了个招呼,“A,你要中断了”,不代表线程A会立即停止自己的工作,同样的A线程完全可以不理会这种中断请求。因为java里的线程是协作式的,不是抢占式的。线程通过检查自身的中断标志位是否被置为true来进行响应,线程通过方法isInterrupted()来进行判断是否被中断,也可以调用静态方法Thread.interrupted()来进行判断当前线程是否被中断,不过Thread.interrupted()会同时将中断标识位改写为false。

	private static class SafeEndThread extends Thread{@Overridepublic void run() {while(!isInterrupted()){ // 检查标志位System.out.println("running");}System.out.println("interrupt");}}// 其他线程中停止safeEndThread线程safeEndThread.interrupt();

注意:SafeEndThread 中如果没有检测isInterrupted()方法,那么调用safeEndThread.interrupt();是无效的。所以java里的线程是协作式的。
如果一个线程处于了阻塞状态(如线程调用了thread.sleep、thread.join、thread.wait),在线程在检查中断标示时如果发现中断标示为true,则会在这些阻塞方法调用处抛出InterruptedException异常,并且在抛出异常后会立即将线程的中断标示位清除,即重新设置为false。

3、线程返回值

Runnable是一个接口,在它里面只声明了一个run()方法,由于run()方法返回值为void类型,所以在执行完任务之后无法返回任何结果。
Callable位于java.util.concurrent包下,它也是一个接口,在它里面也只声明了一个方法,只不过这个方法叫做call(),这是一个泛型接口,call()函数返回的类型就是传递进来的V类型。
Future就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果。必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果。
Future接口包含如下方法,可通过get()获取结果、cancel()取消、isDone()判断是否完成等操作。

  • V get(): 获取结果,若无结果会阻塞至异步计算完成
  • V get(long timeOut, TimeUnit unit):获取结果,超时返回null
  • boolean isDone():执行结束(完成/取消/异常)返回true
  • boolean isCancelled():任务完成前被取消返回true
  • boolean cancel(boolean mayInterruptRunning):取消任务,未开始或已完成返回false,参数表示是否中断执行中的线程

因为Future只是一个接口,所以是无法直接用来创建对象使用的,因此就有了下面的FutureTask。FutureTask类实现了RunnableFuture接口,RunnableFuture继承了Runnable接口和Future接口。所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。
使用例子如下:

	private static class GetReturnCall implements Callable<String>{@Overridepublic String call() throws Exception {System.out.println(" call()");return "GetReturnCallResult";}}GetReturnCall call= new GetReturnCall ();FutureTask<String> futureTask = new FutureTask<>(call);new Thread(futureTask).start();

4、
yield()方法:
使当前线程让出CPU占有权,但让出的时间是不可设定的。也不会释放锁资源,所有执行yield()的线程有可能在进入到可执行状态后马上又被执行。
join方法:
把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B。

三、共享与协作

1、线程间的共享

多个线程处理同一数据,相互配合完成工作,协同处理事情。
Java支持多个线程同时访问一个对象或者对象的成员变量,关键字synchronized可以修饰方法或者以同步块的形式来进行使用,它主要确保多个线程在同一个时刻,只能有一个线程处于方法或者同步块中,它保证了线程对变量访问的可见性和排他性,又称为内置锁机制。

2、线程间的协作

等待/通知机制是指一个线程A调用了对象O的wait()方法进入等待状态,而另一个线程B调用了对象O的notify()或者notifyAll()方法,线程A收到通知后从对象O的wait()方法返回,进而执行后续操作。上述两个线程通过对象O来完成交互,而对象上的wait()和notify/notifyAll()的关系就如同开关信号一样,用来完成等待方和通知方之间的交互工作。上面的方法都是Object的方法,具体作用如下:

  • notify():通知一个在对象上等待的线程,使其从wait方法返回,而返回的前提是该线程获取到了对象的锁,没有获得锁的线程重新进入WAITING状态。
  • notifyAll(): 通知所有等待在该对象上的线程 wait() 调用该方法的线程进入WAITING状态,只有等待另外线程的通知或被中断才会返回.需要注意,调用wait()方法后,会释放对象的锁
  • wait(long):超时等待一段时间,这里的参数时间是毫秒。也就是等待长达n毫秒,如果没有通知就超时返回
  • wait (long,int):对于超时时间更细粒度的控制,可以达到纳秒

wait()、notify()、notifyAll()必须在synchronized中使用,否则会报异常:java.lang.IllegalMonitorStateException: object not locked by thread before notifyAll()

等待方使用如下
1)获取对象的锁。
2)如果条件不满足,那么调用对象的wait()方法,被通知后仍要检查条件。
3)条件满足则执行对应的逻辑。

synchronized(对象){while(条件不满足){对象.wait();}对应的处理逻辑
}

通知方使用如下
1)获得对象的锁。
2)改变条件。
3)通知所有等待在对象上的线程。

synchronized(对象){改变条件对象.notifyAll();
}

注意:等待方和通知方的对象必须是同一个对象

3、wait和sleep的区别

  • wait会释放锁、sleep不会
  • wait是Object的方法,sleep是Thread的方法

四、显示锁

1、Lock

Lock支持的方法如下:
在这里插入图片描述thread调用interrupt方法locakInterruptibly会响应中断抛出异常。

注意Lock使用时一定要释放锁,为了在异常时也可以释放锁,我们应该在finally中释放锁。代码如下:

lock.lock();
try{业务代码
}finally{lock.unlock();
}

2、Lock和synchronized比较

使用synchronized关键字将会隐式地获取锁,但是它将锁的获取和释放固化了,也就是先获取再释放。synchronized属于Java语言层面的锁,也被称之为内置锁
synchronized这种机制,一旦开始获取锁,是不能中断的,也不提供尝试获取锁的机制。
Lock是Java在语法层面提供的,锁的获取和释放需要我们明显的去获取,因此被称为显式锁。并且提供了synchronized不提供的机制。
在这里插入图片描述

3、可重入锁

synchronized关键字隐式的支持重进入,比如一个synchronized修饰的递归方法,在方法执行时,执行线程在获取了锁之后仍能连续多次地获得该锁。
因为Lock是一个接口,Lock的实现类ReentrantLock就是可重入锁。ReentrantLock在调用lock()方法时,已经获取到锁的线程,能够再次调用lock()方法获取锁而不被阻塞。

4、读写锁ReentrantReadWriteLock

之前提到锁(synchronized和ReentrantLock)基本都是排他锁,这些锁在同一时刻只允许一个线程进行访问,而读写锁在同一时刻可以允许多个读线程访问,但是在写线程访问时,所有的读线程和其他写线程均被阻塞。读写锁维护了一对锁,一个读锁和一个写锁,通过分离读锁和写锁,使得并发性相比一般的排他锁有了很大提升。
除了保证写操作对读操作的可见性以及并发性的提升之外,读写锁能够简化读写交互场景的编程方式。假设在程序中定义一个共享的用作缓存数据结构,它大部分时间提供读服务(例如查询和搜索),而写操作占有的时间很少,但是写操作完成之后的更新需要对后续的读服务可见。
一般情况下,读写锁的性能都会比排它锁好,因为大多数场景读是多于写的。在读多于写的情况下,读写锁能够提供比排它锁更好的并发性和吞吐量。
读写锁就是读写分离。

五、公平锁、非公平锁

在时间上,先对锁进行获取的请求一定先被满足,那么这个锁是公平的,反之,是不公平的。公平的获取锁,也就是等待时间最长的线程最优先获取锁,也可以说锁获取是顺序的。
ReentrantLock默认是非公平的,可以通过构造函数设置。synchronized是非公平的。

六、三个特性

1、内存模型

Java内存模型规定所有的变量都是存在主存当中,每个线程都有自己的工作内存。线程对变量的所有操作都必须在工作内存中进行,而不能直接对主存进行操作。并且每个线程不能访问其他线程的工作内存。变量的值何时从线程的工作内存写回主存,无法确定。
在这里插入图片描述

2、三个特性

  • 原子性。一个操作或者一系列操作,要么全部执行要么全部不执行。自增操作++count看上去像一个单独的操作,然而它不是原子操作。自增操作是由三个离散操作构成,获得当前值,加1,写回新值。如果当前count为1 ,两个线线程同时执行++count,可能同时读取count为1,然后增加后为2,但是这里执行两次自增,结果却只加1,所以是有问题的。
        for (int i = 0; i < 10; i++) {new Thread() {@Overridepublic void run() {for (int j = 0; j < 1000; j++) {testAtomicCount++;}Log.d(TAG, "testAtomic>>" + testAtomicCount);}}.start();}

上面例子运行结束testAtomicCount的值不一定是10000,会小于10000。这就是原子性问题造成。

  • 可见性。当一个线程修改了共享属性的值,其它线程能立刻看到共享属性值的更改。举个例子:由于JMM(Java Memory
    Model)分为主存和工作内存,共享属性的修改过程为从主存中读取并复制到工作内存中,在工作内存中修改完成之后,再刷新主存中的值。如果线程A在工作内存中修改完成但还没有刷新主存中的值,线程B看到的值还是旧值。这样可见性就没法保证。
public class VolatileTest {static boolean flag;public static void main(String... args) {new Thread1().start();new Thread2().start();}static class Thread1 extends Thread {@Overridepublic void run() {while (true) {if (flag) {flag = false;System.out.println("Thread1 set flag to false");}}}}static class Thread2 extends Thread {@Overridepublic void run() {while (true) {if (!flag) {flag = true;System.out.println("Thread2 set flag to true");}}}}
}

上面代码就是一个可见性问题的例子。两个线程分别操作flag共享变量,运行结果就是两个线程的日志都不会打印,进入死循环。为什么会这样?两个线程的flag副本分别为false和true,永远都不能进入条件中。这就是因为可见性造成的。如果将flag加上volatile修饰就不会出现上面问题。注意:上面例子在Java环境运行会出现可见性问题,但是在Android环境运行就不会出现,具体原因还需要继续研究。

  • 有序性。程序的运行顺序似乎和我们编写逻辑的顺序是一致的,但计算机在实际执行中却并不一定。为了提高性能,编译器和处理器都会对代码进行重新排序。但是有个前提,重新排序的结果要和单线程执行程序顺序一致。
    static boolean init;static String value="init";public static void main(String... args) {new Thread1().start();new Thread2().start();}static class Thread1 extends Thread {@Overridepublic void run() {value = "hello world";init = true;}}static class Thread2 extends Thread {@Overridepublic void run() {while (!init) {// 等待初始化完成}System.out.println(value.toUpperCase());}}

Thread1中value和init这两个变量之间是没有先后顺序的。如果CPU将这两条指令进行了重排,那么就可能出现初始化已完成,但是value还没有赋值的情况。这样Thread2的while循环就会跳出,然后在操作value的时候打印的不是hello world。

3、synchronized

进入 synchronized 块的内存语义是把在 synchronized 块内使用到的变量从线程的工作内存中清除,这样在 synchronized 块内使用到该变量时就不会从线程的工作内存中获取,而是直接从主内存中获取 。 退出 synchronized 块的内存语义是把在synchronized 块内对共享变量的修改刷新到主内存
synchronized 可以解决共享变量的内存可见性、原子性、有序性 。但是synchronized 关键字会引起线程上下文切换并带来线程调度开销 。

4、volatile

synchronized比较笨重,因为它会带来线程上下文的切换开销 。 对于解决内存可见性问题, Java 还提供了一种弱形式的同步,也就是使用 volatile 关键字
volatile关键字的作用:解决可见性。可以确保对一个变量的更新对其他线程马上可见 。 当一个变量被声明为 volatile 时,线程在写入变量时不会把值缓存在寄存器或者其他地方,而是会把值刷新回主内存 。当其他线程读取该共享变量时,会从主内存重新获取最新值,而不是使用当前线程的工作内存中的值。
volatile关键字还有另外一个重要的作用,就是禁止指令重排。

1)、volatile和synchronized的区别:

volatile 只保证可见性、有序性,synchronized保证可见性、有序性和原子性 。

2)、volatile 关键字使用场景:

  • 写入变量值不依赖、变量的当前值时。因为如果依赖当前值,将是获取一计算一写入三步操作,这三步操作不是原子性的,而 volatile不保证原子性(如自增操作++) 。
  • 读写变量值时没有加锁 。因为加锁本身已经保证了内存可见性,这时候不需要把变量声明为volatile 的。

5、CAS

synchronized比较笨重,因为它会带来线程上下文的切换开销 。 对于解决原子性问题, Java 还提供了非阻塞 CAS 算法实现的原子性操作类 AtomicLong。
CAS 即 Compare and Swap,其是 JDK 提供的非阻塞原子性操作 , 它通过硬件保证了比较更新操作的原子性 。JDK 里面的 Unsafe 类提供了一系列的compareAndSwap*方法 ,下面以 compareAndSwapLong 方法为例进行简单介绍。
boolean compareAndSwapLong(Object obj,long valueOffset,long expect, long update)方法 : 其中 compareAndSwap 的意思是比较并交换。
CAS 有四个操作数 , 分别为 :
对象内存位置 、 对象中的变量的偏移量 、 变量预期值和新的值 。 其操作含义是, 如果对象 obj 中内存偏移量为 valueOffset 的变量值为 expect,则使用新的值 update 替换旧的值 expect 。 这是处理器提供的一个原子性指令

七、乐观锁与悲观锁

1、乐观锁

总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS算法实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量。如Java中java.util.concurrent.atomic包就是使用CAS思想实现
乐观锁写入数据会在死循环中比较主内存值和工作内存保留的值(刚从主存中读取的值)。一样就写入数据到主存然后退出循环。不一样就读取主存值然后重新操作一次,直到不一样。这个又叫自旋,其实就是死循环。

2、悲观锁

总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程。如synchronized和ReentrantLock。

3、乐观锁和悲观锁对比

乐观锁适用于写比较少的情况下(多读场景),即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。但如果是多写的情况,一般会经常产生冲突,这就会导致上层应用会不断的进行retry,这样反倒是降低了性能,所以一般多写的场景下用悲观锁。

八、线程池

1、构造函数

线程池的构造方法如下:
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler)

  • corePoolSize: 线程池中的核心线程数,当提交一个任务时,线程池创建一个新线程执行任务,直到当前线程数等于corePoolSize;如果当前线程数为corePoolSize,继续提交的任务被保存到阻塞队列中,等待被执行;如果执行了线程池的prestartAllCoreThreads()方法,线程池会提前创建并启动所有核心线程。
  • maximumPoolSize: 线程池中允许的最大线程数。如果当前阻塞队列满了,且继续提交任务,则创建新的线程执行任务,前提是当前线程数小于maximumPoolSize
  • keepAliveTime: 线程空闲时的存活时间,即当线程(大于corePoolSize的线程)没有任务执行时,继续存活的时间。默认情况下,该参数只在线程数大于corePoolSize时才有用
  • TimeUnit: keepAliveTime的时间单位
  • workQueue: workQueue必须是BlockingQueue阻塞队列。当线程池中的线程数超过它的corePoolSize的时候,线程会进入阻塞队列进行阻塞等待。通过workQueue,线程池实现了阻塞功能
  • threadFactory: 创建线程的工厂,通过自定义的线程工厂可以给每个新建的线程设置一个具有识别度的线程名Executors静态工厂里默认的threadFactory,线程的命名规则是“pool-数字-thread-数字”
  • RejectedExecutionHandler(饱和策略):线程池的饱和策略,当阻塞队列满了,且没有空闲的工作线程,如果继续提交任务,必须采取一种策略处理该任务,线程池提供了4种策略:
    (1)AbortPolicy:直接抛出异常,默认策略;
    (2)CallerRunsPolicy:用调用者所在的线程来执行任务;
    (3)DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务;
    (4)DiscardPolicy:直接丢弃任务;

2、线程池的工作机制

1)如果当前运行的线程少于corePoolSize,则创建新线程来执行任务(注意,执行这一步骤需要获取全局锁)。
2)如果运行的线程等于或多于corePoolSize,则将任务加入BlockingQueue。
3)如果无法将任务加入BlockingQueue(队列已满),则创建新的线程来处理任务(注意,执行这一步骤需要获取全局锁)。
4)如果创建新线程将使当前运行的线程超出maximumPoolSize,任务将被拒绝,并调用RejectedExecutionHandler.rejectedExecution()方法。
(为什么线程数达到corePoolSize后,不是新建线程而是放在阻塞队列中。因为线程多了不一定会提交效率,线程间切换 保存栈空间会有一定的性能消耗。)


http://chatgpt.dhexx.cn/article/Cs0nOSKp.shtml

相关文章

Android开发中四种常用的多线程实现方式

前言 一般来说&#xff0c;一个应用至少有一个进程&#xff0c;一个进程至少有一个线程。线程是CPU调度的基本单位&#xff0c;进程是系统资源分配的基本单位。 进程拥有独占的内存资源&#xff0c;一个进程可以看作一个JVM一个进程崩溃后&#xff0c;一般不会影响保护模式下…

Android 中的多线程简介

一、概念讲解 进程&#xff1a;是程序运行过程中系统进行资源分配和调度的一个独立单位&#xff0c;使多个程序可 并发执行&#xff0c;以提高系统的资源利用率和吞吐量。 线程&#xff1a;一个基本的CPU执行单元 & 程序执行流的最小单元。 线程自己不拥有系统资源&#…

anchor free和anchor based的区别

链接&#xff1a;https://www.zhihu.com/question/356551927/answer/926659692 1.目标检测算法一般可分为anchor-based、anchor-free、两者融合类&#xff0c;区别就在于有没有利用anchor提取候选目标框。A. anchor-based类算法代表是fasterRCNN、SSD、YoloV2/V3等fasterRCNN-…

Anchor based and Anchor free(无锚VS有锚)【总结】

anchor-free 和 anchor-based 区别 anchor-free和anchor-based是两种不同的目标检测方法&#xff0c;区别在于是否使用预定义的anchor框来匹配真实的目标框。 anchor-based方法使用不同大小和形状的anchor框来回归和分类目标&#xff0c;例如faster rcnn、retinanet和yolo等。a…

2 anchor-base和anchor_free两者的优缺点

anchor-base和anchor_free两者的优缺点 anchor-base和anchor_free两者的优缺点 一、什么是anchor二、anchor-base和anchor-free的区别三、anchor-free和single anchor三、anchor-base和anchor-free的优缺点 参考 一、什么是anchor 从字面的意思解释&#xff0c;anchor就是船锚…

Anchor-Free总结

目录 Anchor-Free综述 一. CornerNet 1.1 概述1.2 模块介绍 1.2.1 Heatmap1.2.2 Offset1.2.3 Grouping Corners1.2.4 Corner Pooling1.3 总结二. CenterNet 2.1 概述2.2 Center-Regression三. FCOS 3.1. 概述3.2. 模块介绍 3.2.1 论文思路简介3.3.2 回归形式3.3 参考文献四 ATS…

【AI面试】Anchor based 、 Anchor free 和 no anchor 的辨析

深度学习的目标检测算法,通常会在输入图像中采样大量的区域,然后判断这些区域中是否包含我们感兴趣的目标,并调整(回归)区域边界,从而更准确地预测目标的真实边界框(ground-truth bounding box)。 目标检测算法会需要做两个事情: 推荐区域框是否有目标(positive or …

一文读懂anchor-base和anchor-free

1. 从Faster-RCNN看Anchor Faster-RCNN相对于Fast-RCNN的一个改进是引入了RPN网络&#xff0c;RPN用于区域推荐&#xff0c;替换了此前的SS算法使得网络在整体上更加的CNN化。那么RPN是怎么进行区域推荐的&#xff1f; 简单来说RPN先列举出数万个矩形框&#xff0c;然后用卷积…

目标检测3--AnchorFree的FCOS

文章目录 1.介绍2.FCOS中使用的方法2.1 网络结构2.2FCOS中使用FPN的多级预测2.3FCOS中的中心度 3.mmdetection中FCOS源码参考资料 欢迎访问个人网络日志&#x1f339;&#x1f339;知行空间&#x1f339;&#x1f339; 1.介绍 论文:《FCOS: Fully Convolutional One-Stage Obj…

浅谈Anchor-Free发展历程

1.早期探索&#xff1a; DenseBox: https://arxiv.org/abs/1509.04874 YOLO: https://arxiv.org/abs/1506.02640 2.基于关键点&#xff1a; CornerNet: https://arxiv.org/abs/1808.01244 ExtremeNet: https://arxiv.org/abs/1901.08043 3.密集预测: FSAF: https://arxiv.org/a…

Anchor-Free系列之FCOS:A Simple and Strong Anchor-free Object Detector

Anchor-Free系列之CornerNet: Detecting Objects as Paired Keypoints_程大海的博客-CSDN博客 Anchor-Free系列之CenterNet&#xff1a;Objects as Points_程大海的博客-CSDN博客 Anchor-Free系列之FCOS&#xff1a;A Simple and Strong Anchor-free Object Detector_程大海的…

Anchor Based和Anchor Free

Anchor Based和Anchor Free之间区别主要有以下两点&#xff1a;1.分类差异&#xff08;关键正负样本定义&#xff09;2.回归差异 1.分类差异&#xff1a; 现阶段的算法多尺度预测&#xff0c;即GT是由哪一个特征层和位置Anchor预测。 Anchor Based是由IoU来确定哪层和哪个位置…

解读《Bridging the Gap Between Anchor-based and Anchor-free Detection》

张士峰大佬近期发了一篇论文解读Anchor-base和Anchor-free方法间的差别&#xff0c;其本质在于正负样本的选取方式不同。 论文&#xff1a;《Bridging the Gap Between Anchor-based and Anchor-free Detection via Adaptive Training Sample Selection》 链接&#xff1a;ht…

anchor-free方法总结

cornernet&#xff0c;centernet&#xff0c;onenet&#xff0c;fcos 这几篇论文的引用关系&#xff08;提出先后顺序&#xff09;&#xff1a; 将按照上面的顺序&#xff0c;从背景、标签分配等方面说明区别于联系。 一、背景&#xff1a; Cornernet&#xff1a;认为使用a…

anchor free和anchor base

仅供个人学习使用 1、anchor base anchor base的方法需要先在图片上生成候选框&#xff0c;无论是RPN生成还是通过k-means生成的先验框&#xff0c;都需要在分类回归之前有存在的框可使用。在框的基础上进行之后的操作。 超参数较为难调&#xff0c;正负样本不平衡&#xff…

Anchor free的心得

问题&#xff1a; 没有了Anchor框的监督信息&#xff0c;我们怎么针对检测任务做到正确回归&#xff1f; 本质&#xff1a;样本与ground truth的对应&#xff0c;如何选择合适样本与真实场景对应 Anchor&#xff1a; 其加入降低了回归问题难度&#xff0c;为分类问题提供选择…

Anchor-based 与 Anchor-free

参考 Anchor-based 与 Anchor-free - 云社区 - 腾讯云 1. Feature Selective Anchor-Free Module for Single-Shot Object Detection 参考&#xff1a;CVPR2019 | CMU提出Single-Shot目标检测最强算法&#xff1a;FSAF 2. FCOS: Fully Convolutional One-Stage Object Det…

Anchor-free

找到了一个说在工业领域很好的 目标检测 下面几篇paper有异曲同工之妙&#xff0c;开启了anchor-based和anchor-free的轮回。 1. Feature Selective Anchor-Free Module for Single-Shot Object Detection 2. FCOS: Fully Convolutional One-Stage Object Detection 3. Fo…

AnchorFree系列算法详解

目录 前言一、Anchor-Based方法回顾二、Anchor Free系列方法简介1. Anchor Free系列算法历史2. Anchor free经典算法详解2.1. 基于关键点的Anchor Free检测算法1. CornerNet 2. 2 基于中心的Anchor Free检测算法1. FCOS2. CenterNet3. TTFNet -- CenterNet的改进版 3. AnchorFr…

目标检测算法——anchor free

一、anchor free 概述 1 、 先要知道anchor 是什么&#xff08;这需要先了解二阶段如faster rcnn&#xff0c;一阶检测器如YOLO V2以后或SSD等&#xff09;。 在过去&#xff0c;目标检测通常被建模为对候选框的分类和回归&#xff0c;不过&#xff0c;按照候选区域的产生方式不…