Android面试七(线程及线程间通信),2024年最新阿里面试p6

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip204888 (备注Android)
img

正文

(3)简单管理线程,可以定时执行或者间隔时间段执行

来源于Java中的空接口Executor,线程池真正实现的是ThreadPoolExecutor


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

         this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, defaultHandler);

}

corePoolSize:核心线程数,默认情况下,及时是闲置状态,也一直存活。当把ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true时,当闲置的核心线程等待新任务时间超过keepAliveTime时,会被自动回收。

maximunPoolSize:最大线程数,超过这个数量时,新任务会被阻塞。

keepAliveTime:非核心线程数闲置的时间

unit:keepAliveTime的时间单位

workQueue:线程池中的任务队列

threadFactory:线程工厂,为线程池提供创建新线程的功能。

种类:

FixedThreadPool(可控最大并发数线程池):线程数量固定,即使闲置状态也不会被回收,除非线程池关闭,只有核心线程,没有非核心线程。


newFixedThreadPool(int nThreads, ThreadFactory threadFactory){

return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,

                                          new LinkedBlockingQueue<Runnable>(),threadFactory);

}

CachedThreadPool(可回收缓存线程池):线程数量不固定,只有非核心线程,当线程都活跃时,就创建新线程,否则利用空闲线程,空闲线程超过60s时会被回收。适合处理大量且耗时较少的任务。


newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,

                                       new SynchronousQueue<Runnable>());

}

scheduleThreadPool(支持定时与周期性任务的线程池):核心线程固定,非核心线程不固定,当非核心线程空闲时,立即被回收。适合执行定时任务或有固定周期的任务。

SingleThreadExecutor(单线程化线程池):只有1个核心线程,没有非核心线程,确保所有任务能够在同一线程且按顺序执行,不用考虑同步问题


    private static void testExecutor(){

        //首先创建线程池,这儿拿单线程化线程池举例

        ExecutorService executorService = Executors.newSingleThreadExecutor();



        Thread thread1 = new Thread(() -> {

            System.out.println(System.currentTimeMillis()+"首先执行线程1");

        });

        Thread thread2 = new Thread(() -> {

            try {

                Thread.sleep(1000);

            } catch (InterruptedException e) {

                e.printStackTrace();

            }

            System.out.println(System.currentTimeMillis()+"然后执行线程2");

        });

        Thread thread3 = new Thread(() -> {

            System.out.println(System.currentTimeMillis()+"最后执行线程3");

        });

        //提交线程

        executorService.submit(thread1);

        executorService.submit(thread2);

        executorService.submit(thread3);

        //关闭线程池

        executorService.shutdown();

    }

输出结果:会按顺序依次执行

1588349278957首先执行线程1

1588349279959然后执行线程2

1588349279959最后执行线程3

7,handlerThread的使用和原理

继承Thread,相当于在Thread中生成了一个loop循环器,可以进行Looper循环,即handlerThread=handler+Thread+looper。

HandlerThread继承Thread后,重写了run方法,可以看到,显示通过Looper.prepare()创建线程,然后采用同步代码,保证线程安全,一个线程没有执行完,则looper就是阻塞状态,执行完后,通过notifyAll()方法通知线程进入就绪状态:

public void run() {

mTid = Process.myTid();

Looper.prepare();

synchronized (this) {

mLooper = Looper.myLooper();

notifyAll();

}

Process.setThreadPriority(mPriority);

onLooperPrepared();

Looper.loop();

mTid = -1;

}

通知的就是getLooper()方法,如果Looper轮询器还在工作,则让线程等待,知道run方法执行notifyall()通知后,,才会返回Looper。

public Looper getLooper() {

if (!isAlive()) {

return null;

}

// If the thread has been started, wait until the looper has been created.

synchronized (this) {

while (isAlive() && mLooper == null) {

try {

wait();

} catch (InterruptedException e) {

}

}

}

return mLooper;

}

特点:

(1)通过获取handlerThread的loop对象,传递给handler对象,在handlerMessage方法中执行异步任务

(2)不会阻塞,减少对性能消耗。但是不能同时多任务处理,需等待处理。

(3)和线程池注重并发不同,只是串行队列,内部只有一个线程。

使用:

(1)先写一个类TestHandler继承Handler,并重写handlerMessage方法

(2)初始化HandlerThread,并启动

HandlerThread handlerThread = new HandlerThread(“test_thread”);

handlerThread.start();//必须得先启动线程,不然没有创建handler,也就没有Looper对象

TestHandler handler = new TestHandler(handlerThread.getLooper());

handler.sendEmptyMessage(0);

(3)当不需要的时候记得调用HandlerThread.quit()方法移除可能未完成的操作避免内存泄露等危险养成一种良好的习惯。

8.IntentService和Service有什么区别

IntentService继承Service,两个都是服务。IntentService可以执行耗时操作,Service不能直接执行耗时操作,否则会ANR。

(1)创建一个测试类继承IntentService并重写onHandleIntent方法,用来处理相应事务

public class TestIntentService extends IntentService {…重写onHandleIntent(Intent intent)方法…}

(2)正常启动service即可

startService(new Intent().setClass(MainActivity.this, TestIntentService.class).putExtra(“content”, “传输的数据”));

(3)在IntentService的onCreate方法中会开启一个HandlerThread线程,内部loop会创建一个ServiceHandler,在onstart方法中,会把intent转到ServiceHandler中,

public void onCreate() {

super.onCreate();

HandlerThread thread = new HandlerThread(“IntentService[” + mName + “]”);

thread.start();

mServiceLooper = thread.getLooper();

mServiceHandler = new ServiceHandler(mServiceLooper);

}

public void onStart(Intent intent, int startId) {

Message msg = mServiceHandler.obtainMessage();

msg.arg1 = startId;

msg.obj = intent;

mServiceHandler.sendMessage(msg);

}

(4)ServiceHandler继承Handler,重写了handleMessage方法,可以看到调用了我们一开始重写的onHandleIntent方法,执行完毕后stopSelf结束service。

private final class ServiceHandler extends Handler {

public ServiceHandler(Looper looper) {

super(looper);

}

@Override

public void handleMessage(Message msg) {

onHandleIntent((Intent)msg.obj);

stopSelf(msg.arg1);

}

}

Service不能直接执行耗时操作,否则会ANR,只能新开线程执行耗时操作。

二,异步消息机制Handler

1,异步消息机制

总共由四部分组成:handler,message,MessageQueue,Looper

(1)在主线程中创建 Handler 对象,并重写 handleMessage() 方法。

(2)子线程进行 UI 操作时,创建 Message 对象,通过第一步创建的Handler 发送消息,handler.sendMessage(message),handler将消息发送到MessageQueue中。

(3)Looper 通过loop()循环从 MessageQueue 中取出待处理消息。

(4)looper将取出的message分发回 Handler 的 handleMessage() 方法中处理。

原理:

https://blog.csdn.net/fnhfire_7030/article/details/79518819

2,什么是handler

handler通过发送和处理Message和Runnable对象来关联相对应线程的MessageQueue。

(Message传递的是消息,MessageQueue是一个消息队列,Handler机制里Lopper轮询器会不断的从消息队列里获取消息,交给handler处理消息)

(1),可以让对应的message和runnable在未来的某个时间点进行相应的处理

(2),让自己想要处理的耗时操作放在子线程,让更新UI的操作放在主线程。

3,handler的使用(post(runnable)和 sendMessage(mesage))

(1),post(runnable)

(1.1)首先创建Handler()

Handler handler = new Handler();

(1.2)然后新开线程,创建Runnable对象,通过post方法,将runnable传到handler当中,handler会在合适的时候让主线程运行runnable更新UI的代码。


class DownloadThread extends Thread{

    @Override

    public void run() {

        try{

            System.out.println("开始下载文件");

            Thread.sleep(2000);

            System.out.println("文件下载完成");

            Runnable runnable = new Runnable() {

                @Override

                public void run() {

                    //更新UI的一些操作

                }

            };

        }catch (InterruptedException e){

            e.printStackTrace();

        }

    }

}

(1.3)在主线程中开启DownloadThread线程

DownloadThread downloadThread = new DownloadThread();

downloadThread .start();

(2),sendMessage(mesage)

(2.1)创建handler,并复写handleMessage方法


private Handler handler = new Handler(){

    @Override

    public void handleMessage(@NonNull Message msg) {

        switch (msg.what){

            case 1:

                //执行UI操作

                break;

        }

    }

};

(2.2)在新开线程中创建message对象,通过what,org1,org2进行赋值,然后handler发送该消息,handler通过其handleMessage的方法进行UI的处理


class DownloadThread extends Thread{

    @Override

    public void run() {

        try{

            System.out.println("开始下载文件");

            Thread.sleep(2000);

            System.out.println("文件下载完成");

            Message msg = new Message();

            msg.what = 1;//自定义的识别码,handler可以识别不同的额msg

            handler.sendMessage(msg);

        }catch (InterruptedException e){

            e.printStackTrace();

        }

    }

}

(2.3)在主线程中开启DownloadThread线程

DownloadThread downloadThread = new DownloadThread();

downloadThread .start();

4,handler的内部机制原理

Handler:

创建handler对象时,则持有了当前线程的Looper和MessageQueue对象

public Handler(Callback callback, boolean async) {

mLooper = Looper.myLooper();

if (mLooper == null) {

throw new RuntimeException(

“Can’t create handler inside thread that has not called Looper.prepare()”);

}

mQueue = mLooper.mQueue;

}

Looper:

每个线程都有的,通过prepare()创建Looper对象,保存在本线程的ThreadLocal中,保证每个线程的Looper的唯一性。

private static void prepare(boolean quitAllowed) {

if (sThreadLocal.get() != null) {

throw new RuntimeException(“Only one Looper may be created per thread”);

}

sThreadLocal.set(new Looper(quitAllowed));

}

private Looper(boolean quitAllowed) {

mQueue = new MessageQueue(quitAllowed);

mThread = Thread.currentThread();

}

通过loop()开启循环,不断调用MessageQueue.next()方法获取Message,进行消息的分发msg.target.dispatchMessage(msg),msg这个属性target就是Handler,其实就是通过handler的dispatchMessage()方法发送消息给消息队列,最终调用handleMessage()。

5,handler引起的内存泄漏

当Activity要回收时,handler没有被回收,对Activity的引用也不会释放。即静态内部类持有外部类的匿名引用,导致外部Activity无法释放。

解决方法:handler内部持有外部activity的弱引用,并把handler改为静态内部类,在activity的onDestroy方法中调用mHandler.removeCallback()方法。

private Handler mHandler = new WeakHandler(this);

static class WeakHandler extends Handler {

private WeakReference mActivity;

TestHandler(Activity activity) {

mActivity = new WeakReference<>(activity);

}

@Override

public void handleMessage(Message msg) {

super.handleMessage(msg);

Activity activity = mActivity.get();

if (activity != null) {

//TODO:

}

}

}

6,handlerThread

1,产生背景:

开启Thread子线程进行耗时操作时,多次创建和销毁线程很耗系统资源。

2,本质

handler+Thread+Looper

本质上是一个线程类,继承了Thread;

但他有自己的内部Looper对象,可以进行looper循环;

通过获取HandlerThread的looper对象传递给Handler对象,可以在handleMessage方法中执行异步任务;

优点是不会堵塞,减少对性能的消耗,缺点是不能同时进行多任务处理,处理效率低。

与线程池注重并发不同,HandlerThread是一个串行队列,背后只有一个线程。

三,面试时会问到的问题

1,简要说下进程和线程的区别

进程是程序运行和资源分配的基本单位,一个程序至少有一个进程,进程在执行过程中拥有独立的内存单元;

线程是CPU调度和分派的基本单位,是能独立运行的基本单位,一个进程至少有一个线程,多个线程共享进程的内存资源,但每个线程都拥有单独的栈内存用来存储本地数据。

2,Thread类中的start()方法和run()方法的区别

start方法用来启动线程,内部调用了run方法,run方法只是直接调用run方法,并没有开启新线程。

3,在多线程中,什么是上下文切换?

上下文切换时存储和恢复CPU状态的过程,它使得线程执行能够从中断点恢复执行。上下文切换是多任务操作系统和多线程环境的基本特征。

4,有三个线程,如果保证线程顺序执行?

(方法1)在线程3调用t1.join()方法,在线程2调用t1.join()方法。

(方法2)使用synchronized锁住Object对象,然后使Object的wait方法使当前线程进入等待状态并释放锁(对象监视器),在要执行的线程执行完任务后调用notify方法,等待该线程剩余代码执行完毕后释放锁(对象监视器),然后唤醒之前进入等待状态的线程。

(方法3)使用SingleThreadExecutor(单线程化线程池)

5,产生死锁的条件

死锁指两个以上的线程永远阻塞的状态。

(1)互斥条件:一个资源每次只能被一个进程使用

(2)请求与保持条件:一个进程因请求资源而阻塞时,对已经获得的资源保持不放。

(3)循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

6,什么是线程局部变量ThreadLocal?

ThreadLocal用于创建线程的本地变量,属于线程自身所有,这是一种实现线程安全的方式。一个对象的所有线程都会共享它的全局变量,所以这些线程是不安全的,我们可以使用同步技术。但是当我们不想使用同步的时候,可以选择ThreadLocal变量,因为ThreadLocal是一种以空间换时间的做法在每个Thread内部维护了一个ThreadLocal,ThreadLocalMap把数据进行隔离,数据不共享,自然就不会有线程安全的问题了。

7,简要说下线程间通信的原理

通过Handler机制

(1)在Handler构造方法中:Looper通过Looper.myLooper()方法获取一个Looper对象,通过mLooper.mQueue拿到与这个Looper对应的MessageQueue;

(2)然后开启死循环,对消息队列进行不停的获取,获取到一个消息后,交给Message.target.dispatchMessage()方法对消息进行处理。

8,post和sendMessage的区别

sendMessage一般和handleMessage配合使用,在sendMessage中发送消息,在handleMessage中接收消息并进行UI的处理;

post方法直接在runnable的run方法中更新UI,但两者本质上没有区别,都是发送消息到消息队列中,只是post的方式更简单些。


public final boolean post(Runnable r)

{

   return  sendMessageDelayed(getPostMessage(r), 0);

}

//获得了message实例,将r赋给callback,接下来还是和sendMessage一致的操作,进入sendMessageDelayed

 private static Message getPostMessage(Runnable r) {

    Message m = Message.obtain();

    m.callback = r;

    return m;

}

public final boolean sendMessageDelayed(Message msg, long delayMillis)

{

    if (delayMillis < 0) {

        delayMillis = 0;

    }

    //最终还是执行到sendMessageAtTime这个方法里面

    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);

}



//接下来看下sendMessageAtTime方法

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {

    MessageQueue queue = mQueue;

    if (queue == null) {

        RuntimeException e = new RuntimeException(

                this + " sendMessageAtTime() called with no mQueue");

        Log.w("Looper", e.getMessage(), e);

        return false;

    }

    return enqueueMessage(queue, msg, uptimeMillis);

}



接下来看下post和sendMessage是如何进行消息的处理的


public void dispatchMessage(Message msg) {

  //如果是post,callback不为空,直接进入handleCallback

    if (msg.callback != null) {

        handleCallback(msg);



### 最后

我的面试经验分享可能不会去罗列太多的具体题目,因为我依然认为面试经验中最宝贵的不是那一个个具体的题目或者具体的答案,而是结束面试时,那一刻你的感受以及多天之后你的回味~

**很多人在刚接触这个行业的时候或者是在遇到瓶颈期的时候,总会遇到一些问题,比如学了一段时间感觉没有方向感,不知道该从那里入手去学习,对此我整理了一些资料,需要的可以免费分享给大家**

在这里小编分享一份自己收录整理上述技术体系图相关的几十套**腾讯、头条、阿里、美团等公司的面试题**,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含**知识脉络 + 诸多细节**,由于篇幅有限,这里以图片的形式给大家展示一部分。

**【Android核心高级技术PDF文档,BAT大厂面试真题解析】**

![](https://img-blog.csdnimg.cn/img_convert/56935ec5478c9d2db591cdd242e0740c.webp?x-oss-process=image/format,png)

**【算法合集】**

![](https://img-blog.csdnimg.cn/img_convert/9ffa8fe82af977175b93792c2fecaf23.webp?x-oss-process=image/format,png)

**【延伸Android必备知识点】**

![](https://img-blog.csdnimg.cn/img_convert/a520dddfaaec5e0a5ba05542af96362d.webp?x-oss-process=image/format,png)

**【Android部分高级架构视频学习资源】**

**Android精讲视频领取学习后更加是如虎添翼!**进军BATJ大厂等(备战)!现在都说互联网寒冬,其实无非就是你上错了车,且穿的少(技能),要是你上对车,自身技术能力够强,公司换掉的代价大,怎么可能会被裁掉,都是淘汰末端的业务Curd而已!现如今市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水,赶快领取吧!



**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注Android)**
![img](https://img-blog.csdnimg.cn/img_convert/d06893f083a6bd03869f4aceac6086e1.png)

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

具体的题目或者具体的答案,而是结束面试时,那一刻你的感受以及多天之后你的回味~

**很多人在刚接触这个行业的时候或者是在遇到瓶颈期的时候,总会遇到一些问题,比如学了一段时间感觉没有方向感,不知道该从那里入手去学习,对此我整理了一些资料,需要的可以免费分享给大家**

在这里小编分享一份自己收录整理上述技术体系图相关的几十套**腾讯、头条、阿里、美团等公司的面试题**,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含**知识脉络 + 诸多细节**,由于篇幅有限,这里以图片的形式给大家展示一部分。

**【Android核心高级技术PDF文档,BAT大厂面试真题解析】**

[外链图片转存中...(img-vjzWb0m5-1713239623911)]

**【算法合集】**

[外链图片转存中...(img-bJddjr79-1713239623911)]

**【延伸Android必备知识点】**

[外链图片转存中...(img-Ro2LKHSD-1713239623912)]

**【Android部分高级架构视频学习资源】**

**Android精讲视频领取学习后更加是如虎添翼!**进军BATJ大厂等(备战)!现在都说互联网寒冬,其实无非就是你上错了车,且穿的少(技能),要是你上对车,自身技术能力够强,公司换掉的代价大,怎么可能会被裁掉,都是淘汰末端的业务Curd而已!现如今市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水,赶快领取吧!



**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注Android)**
[外链图片转存中...(img-Trgj8275-1713239623912)]

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值