Android并发编程高级面试题汇总(含详细解析 十)

Android并发编程高级面试题汇总最全最细面试题讲解持续更新中👊👊
👀你想要的面试题这里都有👀
👇👇👇

什么是守护线程?你是如何退出一个线程的?

这道题想考察什么?

是否了解守护线程与真实场景使用,是否熟悉线程退出该如何操作的本质区别?

考察的知识点

守护线程与线程退出的概念在项目中使用与基本知识

考生应该如何回答

守护线程

在开发过程中,直接创建的普通线程为用户线程,而另一种线程,也就是守护线程,通过setDaemon(true)将一个普通用户线程设置为守护线程。

守护线程,也叫Daemon线程,它是一种支持型、服务型线程,主要被用作程序中后台调度以及支持性工作,跟上层业务逻辑基本不挂钩。Java中垃圾回收线程就是一个典型的Daemon线程。

public class DaemonThreadTest {    
    public static void main(String[] args) {        
        // 创建线程        
        Thread daemonThread = new Thread("Daemon") {            
            @Override            
            public void run() {                
                super.run();                
                // 循环执行5次(休眠一秒,输出一句话)                
                for (int i = 0; i < 5; i++) {                    
                    try {                        
                        Thread.sleep(1000);                    
                    } catch (InterruptedException e) {                        
                        e.printStackTrace();                    
                    }                    
                    System.out.println("守护线程->" + Thread.currentThread().getName() + "正在执行");                		  }            
            }        
        };        
        // 设置为守护线程,必须在线程启动start()方法之前设置,否则会抛出IllegalThreadStateException异常  
        daemonThread.setDaemon(true);        
        // 启动守护线程        
        daemonThread.start();        
        // 这里是主线程逻辑        
        // 循环执行3次(休眠一秒,输出一句话)        
        for (int i = 0; i < 3; i++) {            
            try {                
                Thread.sleep(1000);            
            } catch (InterruptedException e) {                
                e.printStackTrace();            
            }            
            System.out.println("普通线程->" + Thread.currentThread().getName() + "正在执行");        
        }    
    }
}

在主线程里创建守护线程,然后一起执行,主线程的业务逻辑是每隔1s,输出一句log,总共循环三次,而守护线程里也是隔1s输出一句log,不过想要循环5次。其中需要注意的是设置为守护线程,必须在线程启动start()方法之前设置,否则会抛出IllegalThreadStateException异常,意思就是运行中的线程不能设置成守护线程的。最后我们运行一下main方法,看看控制台的输出结果如下:

控制台的输出普通线程->
main正在执行守护线程->
Daemon正在执行守护线程->
Daemon正在执行普通线程->
main正在执行守护线程->
Daemon正在执行普通线程->
main正在执行BUILD SUCCESSFUL in 3s2 actionable tasks: 2 executed

从上面的日志来看,两个线程都只是输出了3次,然后就执行结束,这其实就是守护线程的特点,用一句话概括就是,当守护线程所守护的线程结束时,守护线程自身也会自动关闭。

线程如何退出

要知道当Thread的run方法执行结束后,线程便会自动退出,生命周期结束,这属于线程正常退出的范畴。在实际的开发过程中,还存在另一种业务场景,因为某种原因,需要停止正在运行的线程,那该怎么办呢?

在Thread中提供了一个stop方法,stop方法是JDK提供的一个可以强制关闭线程的方法,但是不建议使用!而且Android针对JDK中的Thread进行了修改,在Android的Thread中调用stop方法会直接抛出异常:

@Deprecated
public final synchronized void stop(Throwable obj) {
	throw new UnsupportedOperationException();
}

那么当我们需要停止运行的线程时,可以通过标识位的方式实现:

使用标志位退出线程

线程执行run方法过程中,我们可以通过一个自定义变量来决定是否还需要退出线程,若满足条件,则退出线程,反之继续执行:

下面代码:注释已经加的很详细了,直接从日志的输出结果来看,很完美的停止了子线程的运行,很OK

public class StopThreadTest {    
    // 定义标志位,使用volatile,保证内存可见    
    private static volatile boolean stopFlag = false;    
    public static void main(String[] args) {        
        // 创建子线程        
        Thread thread = new Thread() {            
            @Override            
            public void run() {                
                super.run();                
                // 循环打印运行日志                
                while (!stopFlag) {                    
                    System.out.println(currentThread().getName() + " is running");                
                }                
                // 退出后,打印退出日志                
                if (stopFlag) {                    
                    System.out.println(currentThread().getName() + " is stop");                
                }            
            }        
        };        
        thread.start();        
        // 让子线程执行100ms后,将stopFlag置为true        
        try {            
            Thread.sleep(100);        
        } catch (InterruptedException e) {            
            e.printStackTrace();        
        }        
        stopFlag = true;    
    }
}
// logcat日志// Thread-0 is running// Thread-0 is running// Thread-0 is running// Thread-0 is running// Thread-0 is running// Thread-0 is running// Thread-0 is running// Thread-0 is running// Thread-0 is running// Thread-0 is running// Thread-0 is stop
使用interrupt方法中断线程

使用interrupt方法来中断线程,本质上也是标志位的方式。跟上面的原理一样,只不过是Thread类内部提供的罢了。其他线程通过调用某个线程的interrupt()方法对其进行中断操作,被中断的线程则是通过线程的isInterrupted()来进行判断是否被中断。

public class StopThreadTest {    
    public static void main(String[] args) {        
        // 创建子线程        
        Thread thread = new Thread() {            
            @Override            
            public void run() {                
                super.run();                
                // 通过isInterrupted()方法判断线程是否已经中断                
                while (!isInterrupted()) {                    
                    // 打印运行日志                    
                    System.out.println(currentThread().getName() + " is running");                
                }                
                if (isInterrupted()) {                    
                    // 打印退出日志                    
                    System.out.println(currentThread().getName() + " is stop");                
                }            
            }        
        };        
        thread.start();        
        // 让子线程执行100ms后,将stopFlag置为true        
        try {            
            Thread.sleep(100);        
        } catch (InterruptedException e) {            
            e.printStackTrace();        
        }        
        // 通过调用interrupt方法去改变标志位        
        thread.interrupt();    
    }
}

sleep 、wait、yield与join的区别,wait 的线程如何唤醒它?(字节跳动)

这道题想考察什么?

  1. 是否了解Java中线程相关的知识点
  2. 线程的生命周期

考察的知识点

  1. Java中线程的相关概念
  2. Sleep、yield、wait和jion函数的区别
  3. 多线程并发相关的知识点

考生应该如何回答

sleep 、wait、yield与join的区别

sleep、yield与join是线程方法,而wait则是Object方法:

  • sleep **,释放cpu资源,不释放锁资源,**如果线程进入sleep的话,释放cpu资源,如果外层包有Synchronize,那么此锁并没有释放掉。

  • wait,**释放cpu资源,也释放锁资源,**一般用于锁机制中 肯定是要释放掉锁的,因为notify并不会立即调起此线程,因此cpu是不会为其分配时间片的,也就是说wait 线程进入等待池,cpu不分时间片给它,锁释放掉。

  • yield:让出CPU调度,Thread类的方法,类似sleep只是不能由用户指定暂停多长时间 ,并且yield()方法只能让同优先级的线程有执行的机会。 yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。调用yield方法只是一个建议,告诉线程调度器我的工作已经做的差不多了,可以让别的相同优先级的线程使用CPU了,没有任何机制保证采纳。

  • join:一种特殊的wait,当前运行线程调用另一个线程的join方法,当前线程进入阻塞状态直到另一个线程运行结束等待该线程终止。 注意该方法也需要捕捉异常。

wait 的线程如何唤醒

执行wait方法会导致当前线程进入Blocked状态,可以调用执行wait方法的对象的notify或者notifyAll方法唤醒:

public class Main {
    public static void main(String[] args) throws InterruptedException {
        var q = new TaskQueue();
        var ts = new ArrayList<Thread>();
        for (int i=0; i<5; i++) {
            var t = new Thread() {
                public void run() {
                    // 执行task:
                    while (true) {
                        try {
                            String s = q.getTask();
                            System.out.println("execute task: " + s);
                        } catch (InterruptedException e) {
                            return;
                        }
                    }
                }
            };
            t.start();
            ts.add(t);
        }
        var add = new Thread(() -> {
            for (int i=0; i<10; i++) {
                // 放入task:
                String s = "t-" + Math.random();
                System.out.println("add task: " + s);
                q.addTask(s);
                try { Thread.sleep(100); } catch(InterruptedException e) {}
            }
        });
        add.start();
        add.join();
        Thread.sleep(100);
        for (var t : ts) {
            t.interrupt();
        }
    }
}

class TaskQueue {
    Queue<String> queue = new LinkedList<>();

    public synchronized void addTask(String s) {
        this.queue.add(s);
        this.notifyAll();
    }

    public synchronized String getTask() throws InterruptedException {
        while (queue.isEmpty()) {
            this.wait();
        }
        return queue.remove();
    }
}

在addTask()方法中调用了this.notifyAll()而不是this.notify(),使用notifyAll()将唤醒所有当前正在this锁等待的线程,而notify()只会唤醒其中一个(具体哪个依赖操作系统,有一定的随机性)。

之所以使用notifyAll,是因为可能有多个线程正在getTask()方法内部的wait()中等待,使用notifyAll()将一次性全部唤醒。通常来说,notifyAll()更安全。有些时候,如果我们的代码逻辑考虑不周,用notify()会导致只唤醒了一个线程,而其他线程可能永远处于Blocked状态。


由于面试题内容比较多,篇幅有限,资料已经被整理成了PDF文档,有需要2023年Android中高级最全面试真题答案 完整文档的可扫描下方卡片免费获取~

PS:(文末还有使用ChatGPT机器人小福利哦!!大家不要错过)

目录

在这里插入图片描述

第一章 Java方面

●Java基础部分
●Java集合
●Java多线程
●Java虚拟机

在这里插入图片描述

第二章 Android方面

●Android四大组件相关
●Android异步任务和消息机制
●Android UI绘制相关
●Android性能调优相关
●Android中的IPC
●Android系统SDK相关
●第三方框架分析
●综合技术
●数据结构方面
●设计模式
●计算机网络方面
●Kotlin方面

在这里插入图片描述

第三章 音视频开发高频面试题

●为什么巨大的原始视频可以编码成很小的视频呢?这其中的技术是什么呢?
●怎么做到直播秒开优化?
●直方图在图像处理里面最重要的作用是什么?
●数字图像滤波有哪些方法?
●图像可以提取的特征有哪些?
●衡量图像重建好坏的标准有哪些?怎样计算?

在这里插入图片描述

第四章 Flutter高频面试题

●Dart部分
●Flutter部分

在这里插入图片描述

第五章 算法高频面试题

●如何高效寻找素数
●如何运用二分查找算法
●如何高效解决雨水问题
●如何去除有序数组的重复元素
●如何高效进行模幂运算
●如何寻找最长回文子串

在这里插入图片描述

第六章 Andrio Framework方面

●系统启动流程面试题解析
●Binder面试题解析
●Handler面试题解析
●AMS面试题解析

在这里插入图片描述

第七章 企业常见174道面试题

●SD卡
●Android的数据存储方式
●Broadcast Receiver
●sp频繁操作会有什么后果?sp能存多少数据?
●dvm与jvm的区别
●ART
●Activity的生命周期
●Application能不能启动Activity
●…

在这里插入图片描述

搭建了一个基于chatGPT的微信群聊机器人,24小时为大家解答疑难技术问题, 需要的可以扫描二维码进群。

图片

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值