likes
comments
collection
share

五道Android手写题,动脑动嘴还得动手

作者站长头像
站长
· 阅读数 37

近期为了准备找工作面试,在上一篇文章里面整理了几个Android方面比较常见的面试题,一些八股文,但是现在面试跟以前不太一样了,八股文可能在一场面试里面占的比例不是很多,甚至人家不问,问的多的都是些项目经验啊,或者某个问题怎么解决的,能看出你一定水平的,或者一些编程题,让你现场写代码或者上机敲代码,这种面试题才可以检验出面试者能力究竟如何,所以这次我也整理了一些编程题,希望可以对我,对看过这篇文章的人有所帮助

使用kotlin来实现单例模式

以前让写单例模式的时候,都是要求用java写,但这几年随着kotlin成为了Android开发的主要语言,像这种手写单例模式也必须用kotlin写出来,下面就来看下分别使用五种方式来实现kotlin版本的单例模式

饿汉式

五道Android手写题,动脑动嘴还得动手

饿汉式当初用java实现的时候就已经代码量很少了,而在kotlin中,代码量更少,将一个类的class关键字改成object就是kotlin的饿汉式写法

懒汉式

五道Android手写题,动脑动嘴还得动手

懒汉式跟java里面的懒汉式就比较相似了,将构造器声明为private防止外界访问,函数getInstance()用来获取实例,关键字Synchronized用来保护线程安全

双重检查锁

五道Android手写题,动脑动嘴还得动手

关键字volatile使得变量instance可见性,防止指令重排,并且在创建instance的时候给instance加锁,确保线程安全

静态内部类

五道Android手写题,动脑动嘴还得动手

这种方式是让一个静态内部类持有单例实例,然后利用类加载机制保证线程安全

枚举类

五道Android手写题,动脑动嘴还得动手

由于枚举本身只会被创建一次,所以它是线程安全的

文件名排序

这是之前在某一家外企面试的时候他们出的笔试题,原题具体内容忘记了,大致的一个意思如下所示

五道Android手写题,动脑动嘴还得动手

乍一看,就是一个字符串排序的,感觉太easy了,但是如果你仔细看一下希望输出的结果以后,会一时之间找不出规律,这个既不像是对数字的排序,也不像是对字符串的排序,这个规律似乎很难找,但是我们朝输出结果较靠后的那部分观察一下,会发现,到是的确是按照字符串排序的,唯一区别是,靠后那部分字符串里面是有的带数字,有的不带数字,而比较靠前的字符串里面是都有数字的,而且将字母部分去除的话会发现,它的确是按照数字大小来排序,那么,这个规律就被我们找到了,排序的代码如下

五道Android手写题,动脑动嘴还得动手

首先比较字符串中前后字符串里面的数字部分,如果都有数字而且数字不一样,那么比较数字,如果数字相同就比较字符串,如果前后字符串里面存在不含数字的字符串,那么也是比较字符串

如何实现一个线程安全的计数器

我们知道多线程是有线程安全隐患的,举个例子,如果想要多线程实现一个计数器,如果不考虑线程安全的话会写成下面这样

五道Android手写题,动脑动嘴还得动手

这个预期是想要打印出200,但是实际的结果却不是这样

五道Android手写题,动脑动嘴还得动手

实际只是199,说明上面这个操作是线程不安全的,那么该怎么改呢?

原子类型

第一种方式可以使用原子类型AtomicInteger,在每个线程中使用incrementAndGet方法来自增,代码如下

五道Android手写题,动脑动嘴还得动手 五道Android手写题,动脑动嘴还得动手

使用原子类型就能在多线程中保证线程安全,原理是因为其内部使用了CAS锁,将当前值与目标值先做比较,如果一致就更新当前值为目标值,如果不一致则不做修改

synchronized

第二种方式就是使用同步函数synchronized,代码如下

五道Android手写题,动脑动嘴还得动手

也可以保证线程安全的输出最终值

五道Android手写题,动脑动嘴还得动手

如何实现多线程顺序执行

多线程中,线程之间是通过抢占资源来执行的,所以我们无法确定哪个线程先执行哪个线程后执行,比如下面这段代码

五道Android手写题,动脑动嘴还得动手

这里新建了10个线程并且都让它们执行,打印的日志将线程的下标值同时打印出来,我们可以看下最终结果是怎么样的

五道Android手写题,动脑动嘴还得动手

很明显线程并不是按照创建顺序来执行的,那么如果我们想要让线程按照创建的顺序来执行,该怎么做呢?

使用join

第一个方法使用Thread类里面自带的join方法,我们可以先看下join方法的源码都干了啥事情

五道Android手写题,动脑动嘴还得动手

首先这个方法是同步的,所以可以保证只有一个线程会执行它,然后先方法内,无论是给join设置了还是没设置延迟时间,它都是会在线程执行中,也就是isAlivetrue的时候一直wait,直到isAlive不为true的时候,才不会等待,那么在这里我们只要在启动线程后执行一下join方法,那么每个线程都会等到前一个线程执行完毕再去执行,将代码更改后试一下

五道Android手写题,动脑动嘴还得动手 五道Android手写题,动脑动嘴还得动手

跟预期的一样,线程都是按照顺序执行了

使用FutureTask

有人说不想使用join,就是想单纯的执行线程,然后让它们顺序执行,那该怎么做呢?这里就要使用FutureTask了,FutureTask其实也是个Runnable,所以它也可以作为参数传给Thread,而创建一个FutureTask需要使用Callable,CallableRunnable其实差不多,主要区别是Callable可以将执行结果返回,而想要获取执行结果的话就要使用FutureTaskget方法,这个get方法就是让我们顺序执行线程的关键,看下get里面的源码

五道Android手写题,动脑动嘴还得动手

光看到awaitDone的名字就知道,这个get方法是会等到线程执行完毕后才会释放资源,知道这些后,具体代码就知道怎么写了

五道Android手写题,动脑动嘴还得动手

执行结果如下

五道Android手写题,动脑动嘴还得动手

使用CountDownLatch

现在我们已经用两种方式来实现线程顺序执行了,但是有人会说,我就想用Runnable,难道就不能执行吗?也是可以的,我们可以使用CountDownLatch,这是个计数器,构造函数中的入参表示需要计数的数量,调用await方法使得当前线程等待,调用countDown方法让计数器减一,当计数变为0的时候,就释放当前线程,那么使用计数器的代码如下

五道Android手写题,动脑动嘴还得动手

因为这里有十个线程,所以需要9个CountDownLatch,因为第一个线程不需要await,最后一个线程不需要countDown,最终执行结果也是顺序执行线程

五道Android手写题,动脑动嘴还得动手

如何让三个线程交替打印ABC

想要实现三个线程交替打印ABC,那么就需要使用信号量SemapPhore来实现,每个信号量控制一个线程,当某个信号量被获取时候,也就是调用acquire方法,对应线程就会执行,并且将可用信号量减1,这样其他信号量由于拿不到可用信号,就会陷入阻塞阶段,而当信号量调用release方法的时候,可用信号量就会加一,可用信号量又有了,其他信号量开始获取,具体代码如下

五道Android手写题,动脑动嘴还得动手

运行结果如下

五道Android手写题,动脑动嘴还得动手

总结

五道手写题写完了,个人觉得手写题还是重在一个思路,并不存在什么标准答案,有些手写题可以有多个方式去实现,不过想要锻炼出看到个手写题就有思路这件事,还是要去网上多看一些手写题,算法的解题过程,多看看别人是如何解题的,学习一下那种思考方式,所以终究还是离不开一个积累,温水煮青蛙,慢工出细活,好的offer急不来的

转载自:https://juejin.cn/post/7388842078799003657
评论
请登录