Android 多线程高频面试题及详细答案(实战向)_第1页
Android 多线程高频面试题及详细答案(实战向)_第2页
Android 多线程高频面试题及详细答案(实战向)_第3页
Android 多线程高频面试题及详细答案(实战向)_第4页
Android 多线程高频面试题及详细答案(实战向)_第5页
已阅读5页,还剩4页未读, 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

Android多线程高频面试题及详细答案(实战向)一、基础概念类1.Android中线程和进程的区别?答案:1)进程:是操作系统资源分配的最小单位,每个App独立运行在一个Linux进程中,拥有独立的内存空间、CPU时间片、资源权限。进程之间相互隔离,默认不共享数据,一个进程崩溃不会影响其他进程。2)线程:是CPU调度执行的最小单位,依附于进程存在。一个进程可以包含多个线程,同一进程内的所有线程共享堆内存、全局变量、资源文件等。线程没有独立资源,某个线程异常崩溃,会直接导致所在进程崩溃。3)Android特性:默认情况下,一个App就是一个单进程多线程应用,主线程(UI线程)负责界面刷新、事件分发,子线程专门用于耗时操作。2.什么是主线程?为什么主线程不能做耗时操作?答案:主线程也叫UI线程,是App启动后默认运行的线程,核心职责是处理UI绘制、用户触摸事件、页面刷新、组件生命周期回调等核心界面逻辑。主线程禁止耗时操作(网络请求、文件读写、数据库大批量查询、复杂计算等),原因有两点:1)界面卡顿:Android屏幕刷新频率为60帧/秒,每帧刷新间隔约16ms。如果主线程阻塞、执行耗时逻辑,无法及时完成UI绘制,会导致丢帧,出现界面卡顿、滑动不流畅问题。2)ANR崩溃:系统对主线程有超时监控,按键/触摸事件5秒未响应、Service前台执行20秒超时、BroadcastReceiver10秒未完成,都会触发ANR(应用无响应)弹窗,直接导致App闪退。3.线程的生命周期有哪些状态?答案:Android基于Java线程,线程共5种核心状态:1)新建(NEW):线程对象创建完成,但未调用start()方法,处于初始状态,未分配执行资源。2)就绪(RUNNABLE):调用start()后,线程进入就绪队列,等待CPU调度,具备执行资格,暂时没有CPU执行时间片。3)运行(RUNNING):CPU分配时间片,线程正式执行run()方法中的业务逻辑。5)终止(TERMINATED):run()方法执行完毕,或线程异常终止,线程生命周期结束,无法再次启动。4.sleep()和wait()的核心区别?答案:这是多线程高频基础考点,核心差异集中在锁机制、唤醒方式、所属类、使用场景:1)所属类不同:sleep()是Thread类静态方法;wait()是Object类方法。2)锁释放机制不同:sleep()仅让线程休眠,不会释放所持有的同步锁;wait()会主动释放当前持有的对象锁,进入对象等待池。3)唤醒方式不同:sleep()时间结束后自动唤醒;wait()必须通过notify()/notifyAll()手动唤醒,或设置超时时间自动唤醒。4)使用限制不同:sleep()可在任意线程、任意场景使用;wait()必须在同步代码块/同步方法中调用,否则直接抛出异常。5)异常处理:sleep()需要捕获InterruptedException;wait()同样需要捕获中断异常。二、Android线程通信与Handler机制(核心高频)1.为什么Android设计成子线程不能更新UI?答案:核心目的是保证UI操作的线程安全,避免界面渲染错乱。Android的UI控件不是线程安全的,如果允许多个线程同时操作UI,会出现控件状态覆盖、绘制错乱、参数异常等问题。如果加锁处理UI操作,会大幅增加系统复杂度,同时引发线程阻塞、性能损耗、卡顿问题。因此系统采用单线程UI模型,强制所有UI更新操作统一在主线程执行,规避多线程并发冲突,简化UI渲染逻辑,保证界面绘制稳定。补充:子线程更新UI不是绝对报错,在View未完成绘制、未触发checkThread检测时,偶尔可以更新,但属于不稳定行为,生产环境绝对禁止。2.Handler、Message、MessageQueue、Looper四者关系?答案:四者共同组成Android主线程消息驱动机制,各司其职、相互配合:1)Message:消息载体,用于携带数据、标识任务,包含what、arg1、arg2、obj等字段,可被线程投递和处理。2)MessageQueue:消息队列,底层是单链表结构,用于存储Handler投递的Message,遵循先进先出原则,仅负责存储消息,不处理消息。3)Looper:消息循环器,绑定专属线程,无限循环从MessageQueue中取出消息,分发到对应Handler处理。一个线程只能有一个Looper、一个MessageQueue。主线程默认创建Looper,子线程需要手动初始化Looper.prepare()、Looper.loop()。4)Handler:消息收发、任务处理工具,绑定Looper,负责发送Message到消息队列,同时接收Looper分发的消息,在绑定线程中处理任务。整体流程:子线程通过Handler发送消息->消息存入MessageQueue->Looper循环取消息->Handler回调handleMessage处理消息(主线程执行)。3.主线程Looper.loop()是死循环,为什么不会卡死主线程?答案:这是面试超高频深度问题,核心原因是底层阻塞机制+消息驱动机制:1)loop()虽然是while死循环,但队列中没有消息时,会阻塞休眠,释放CPU资源,不会持续占用CPU。底层通过Linuxepoll机制实现阻塞等待,无消息时线程挂起,不消耗性能。2)当有新消息入队时,系统唤醒线程,loop()继续执行,处理对应UI任务、事件回调。3)主线程所有核心逻辑(Activity生命周期、UI绘制、点击事件、刷新任务)都是通过消息机制驱动的,loop()循环的本质是不断处理系统和App的各类任务,不是空循环阻塞。简单说:无消息则休眠、有消息则执行,不卡顿、不耗性能,是App运行的核心动力。4.Handler内存泄漏原因及解决方案?答案:泄漏原因:1)非静态内部类Handler会隐式持有外部Activity/Fragment的引用;2)Handler发送的延迟消息、循环消息会存入MessageQueue,Message会持有Handler引用;3)当页面销毁时,消息未执行完毕,引用链无法断开,Activity无法被GC回收,造成内存泄漏。解决方案:1)将Handler定义为静态内部类,消除隐式外部引用;2)通过弱引用持有Activity/Fragment对象,保证GC可回收;3)页面销毁(onDestroy)时,调用removeCallbacksAndMessages(null)清空所有消息和任务,彻底断开引用;4)尽量避免发送长时间延迟的消息,减少泄漏风险。5.postDelay延迟消息的底层原理?答案:1)postDelay发送的消息会记录对应的触发时间,不是定时等待,而是直接存入MessageQueue;2)MessageQueue会根据消息触发时间排序,延时越久的消息,队列排序越靠后;3)Looper循环取消息时,会对比当前时间和消息触发时间:未到时间则阻塞等待,到时间后取出消息执行;4)整个过程无需轮询检测,依托底层阻塞机制实现精准延时,性能开销极低。三、线程池相关面试题(实战核心)1.Android为什么推荐用线程池,不推荐newThread?答案:频繁newThread存在诸多性能问题,线程池是官方推荐的耗时任务解决方案:1)资源开销大:新建线程需要操作系统分配资源、创建栈内存、调度初始化,频繁创建销毁线程会产生大量系统开销,导致APP卡顿、帧率下降。2)不可控:手动创建的线程无统一管理,任务并发数量不可控,大量线程同时运行会抢占CPU、内存溢出、ANR。3)无复用机制:newThread执行完即销毁,无法复用,频繁创建销毁加重GC压力。线程池优势:统一管理线程、复用线程、控制最大并发数、减少GC开销、支持任务队列、延迟任务、定时任务,适配绝大多数子线程耗时场景。2.线程池核心参数(ThreadPoolExecutor)详解?答案:核心7大参数,面试重点考察前5个:1)corePoolSize(核心线程数):线程池常驻线程数量,即使空闲也不会被回收,长期保留用于复用。2)maximumPoolSize(最大线程数):线程池允许创建的最大线程总数,核心线程+非核心线程总和不超过该值。3)keepAliveTime(空闲超时时间):非核心线程空闲后的存活时间,超时未接收任务自动回收。4)unit(时间单位):keepAliveTime的时间单位,秒、毫秒、分钟等。5)workQueue(任务队列):存储等待执行的Runnable任务,常用阻塞队列,核心线程满负荷后,新任务进入队列排队。6)threadFactory(线程工厂):用于创建线程,可自定义线程名称、优先级、守护线程属性,方便日志排查。7)handler(拒绝策略):线程池满、队列已满时,新任务的拒绝处理策略。3.线程池任务执行流程?答案:1)提交新任务,首先判断核心线程数是否已满,未满则新建核心线程执行任务;2)核心线程已满,判断任务队列是否已满,队列未满则任务入队等待;3)队列已满,判断最大线程数是否已满,未满则新建非核心线程执行任务;4)最大线程数已满,触发拒绝策略,拒绝执行新任务。4.Android常用的四种内置线程池特点?答案:1)FixedThreadPool(固定线程池):核心线程数=最大线程数,无线程回收,任务队列无大小限制。适合大量、长期、稳定的并发任务,如批量数据处理。2)CachedThreadPool(缓存线程池):无核心线程,最大线程数无限,空闲线程60秒回收。适合大量、短时、零散的突发任务,任务多就新建线程,空闲自动回收。3)SingleThreadPool(单线程池):只有一个核心线程,所有任务串行执行,队列排队。适合需要顺序执行、不能并发冲突的任务,如日志上传、文件写入。4)ScheduledThreadPool(定时线程池):支持延迟执行、周期性执行任务,适合定时轮询、心跳检测、延时任务场景。补充:生产环境不推荐直接使用Executors创建内置线程池,容易出现队列无限扩容、线程无限创建导致OOM,建议自定义ThreadPoolExecutor,手动配置参数。四、并发同步与线程安全1.synchronized和Lock的区别?答案:1)实现方式不同:synchronized是JVM原生关键字,底层依赖对象锁、监视器实现;Lock是Java接口,基于代码层实现锁机制。2)锁释放机制:synchronized自动加锁、自动释放锁,无需手动处理;Lock需要手动lock()加锁、unlock()释放锁,必须在finally中释放,避免死锁。3)锁特性不同:synchronized是非公平锁、可重入锁,无法中断等待锁的线程;Lock(ReentrantLock)支持公平/非公平锁、可重入、可中断等待、可超时获取锁。4)条件等待:synchronized只有一个等待队列;Lock可以创建多个Condition,精准唤醒指定线程,灵活性更高。日常开发:简单同步场景用synchronized,高并发、灵活控制锁场景用ReentrantLock。2.什么是线程死锁?产生死锁的四个必要条件?如何避免?答案:死锁定义:多个线程互相持有对方需要的锁,同时等待对方释放锁,所有线程永久阻塞,任务无法执行,程序卡死。四大必要条件(必须同时满足):1)互斥条件:锁资源同一时间只能被一个线程持有;2)请求与保持条件:线程持有已有锁,同时请求新的锁,不释放原有锁;3)不可剥夺条件:已持有锁的线程,不会被强制剥夺锁资源;4)循环等待条件:多个线程形成循环锁等待链。避免方案:1)破坏请求与保持:一次性申请所有需要的锁;2)破坏循环等待:统一锁的获取顺序,所有线程按固定顺序加锁;3)使用可超时锁:通过Lock的tryLock(timeout),超时自动放弃锁请求;4)尽量减少嵌套锁,从根源规避锁嵌套场景。3.volatile关键字的作用和局限性?答案:核心作用:1)保证可见性:修饰的变量,一个线程修改后,立即刷新到主内存,其他线程可实时读取最新值,避免线程缓存过期;2)禁止指令重排:防止编译器、CPU优化时打乱代码执行顺序,解决多线程指令重排导致的逻辑异常。局限性:不保证原子性。volatile只能保证单读、单写的线程安全,无法保证复合操作(i++、i=i+1)的原子性,这类并发操作仍会出现数据错乱,需要配合锁或Atomic原子类使用。五、Android异步框架与实战问题1.AsyncTask的缺点及废弃原因?答案:AsyncTask在API30正式废弃,核心缺点如下:1)线程池缺陷:早期是单线程串行执行,后期改为多线程,不同版本行为不一致,兼容性差;2)极易内存泄漏:非静态内部类持有页面引用,后台任务未完成页面销毁,引发泄漏;3)任务丢失、生命周期不同步:页面重建(横竖屏切换)后,后台任务继续执行,回调失效、数据错乱;4)功能单一:不支持取消、链式调用、异常捕获,无法满足复杂异步场景;5)串行执行效率低:默认任务排队执行,无法高效处理批量并发任务。目前官方推荐替代方案:Kotlin协程、RxJava、自定义线程池+Handler。2.子线程更新UI的几种合法方式?答案:1)Handler+Message:子线程发送消息,主线程Handler接收更新UI;2)view.post()/view.postDelayed():直接通过控件post任务,自动切换到主线程执行,简洁轻便;3)runOnUiThread():Activity专属方法,子线程中直接调用,切换主线程更新UI;4)协程Dispatchers.Main:Kotlin协程指定主线程调度器,异步任务后刷新UI;5)RxJavaobserveOn(AndroidSchedulers.mainThread())切换主线程。3.线程任务

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论