0%

定时器功能定义

  • 能够延迟执行任务
  • 能够周期性执行某个任务
  • 能够添加多个任务,按照任务执行时间依次执行

单个任务如何延迟执行?

执行线程需要一直运行,等待任务时间到了再执行。

所以需要一个死循环,让线程一直运行下去。

但是没有任务时,死循环会空转CPU,所以需要释放CPU。

如何让线程释放CPU,又让线程不死进行等待?

sleep()或wait(),把CPU交出去。

线程应该等待多久?

sleep()或wait()都可以传递要等待多长时间,等待时长可以用任务时间减去当前时间计算而来。

如何周期性执行一个任务?

任务执行完后,计算新的等待时间,继续让线程等待。

多个任务如何处理任务优先级?

每个任务都要有一个执行时间,按任务执行时间从小到大排序,先执行时间小的任务。

怎么排序?

观察任务添加的特点。

任务可能会动态添加,比如Android中的Handler,消息队列可以随时插入新的消息,并且消息的执行时间完全任意。

这就需要在线排序算法,用优先队列,创建最小堆,往堆中添加一个元素,调整堆只需要O(log n)的时间复杂度。

如果使用离线的基于比较的排序算法,时间复杂度O(n * log n),插入排序时间最短,最多花费O(n),但是都没调整堆时间少。

任务排序好后怎么依次执行?

读取时间最小的任务,把它的执行时间减去当前时间,得到等待时间,让线程sleep或wait这个时间。

等到线程唤醒后,执行任务,从队列中移除这个已执行的任务,取下一个任务,再计算等待时间。

后续任务重复这个流程。

线程在等待执行了,新任务想插队先执行怎么处理?

新任务添加到优先队列后,立刻唤醒执行线程,重新计算要等待的时间,保证执行线程始终等待执行时间最小的时间的任务。

单个任务执行时间过长,导致后面任务没有按时执行怎么办?

开启多个执行线程,多线程从同一个任务队列中取任务,队列中的任务按时间排序好。

相当于设计一个带延迟功能的线程池了,即ScheduledThreadPoolExecutor。

考察点

  • 线程同步工具熟悉和理解程度
  • 代码规范,是否注重代码可读性、整洁性,考察编码习惯
  • 代码熟练度,具体语言掌握情况
  • 能否说出每种实现的原理、细节 注意事项、适用场景

synchronized

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
private const val STATE_A = 1
private const val STATE_B = 2
private const val STATE_C = 3

@Volatile
private var state = STATE_A

fun main() {
val lock = Object()
Thread {
repeat(10) {
synchronized(lock) {
while (state != STATE_A) lock.wait()
println("A")
state = STATE_B
lock.notifyAll()
}
}
}.start()
Thread {
repeat(10) {
synchronized(lock) {
while (state != STATE_B) lock.wait()
println("B")
state = STATE_C
lock.notifyAll()
}
}
}.start()
Thread {
repeat(10) {
synchronized(lock) {
while (state != STATE_C) lock.wait()
println("C")
state = STATE_A
lock.notifyAll()
}
}
}.start()
}

ReentrantLock

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import java.util.concurrent.locks.ReentrantLock
import kotlin.concurrent.withLock

private const val STATE_A = 1
private const val STATE_B = 2
private const val STATE_C = 3

@Volatile
private var state = STATE_A

fun main() {
val lock = ReentrantLock()
val c1 = lock.newCondition()
val c2 = lock.newCondition()
val c3 = lock.newCondition()
Thread {
repeat(10) {
lock.withLock {
while (state != STATE_A) c1.await()
println("A")
state = STATE_B
c2.signal()
}
}
}.start()
Thread {
repeat(10) {
lock.withLock {
while (state != STATE_B) c2.await()
println("B")
state = STATE_C
c3.signal()
}
}
}.start()
Thread {
repeat(10) {
lock.withLock {
while (state != STATE_C) c3.await()
println("C")
state = STATE_A
c1.signal()
}
}
}.start()
}

Sempahore

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import java.util.concurrent.Semaphore

fun main() {
val s1 = Semaphore(1)
val s2 = Semaphore(0)
val s3 = Semaphore(0)
Thread {
repeat(10) {
s1.acquire()
println("A")
s2.release()
}
}.start()
Thread {
repeat(10) {
s2.acquire()
println("B")
s3.release()
}
}.start()
Thread {
repeat(10) {
s3.acquire()
println("C")
s1.release()
}
}.start()
}

CountDownLatch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import java.util.concurrent.CountDownLatch

fun main() {
var latchA = CountDownLatch(1)
var latchB = CountDownLatch(1)
var latchC = CountDownLatch(1)
Thread {
repeat(10) {
latchA.await()
latchA = CountDownLatch(1)
println("A")
latchB.countDown()
}
}.start()
Thread {
repeat(10) {
latchB.await()
latchB = CountDownLatch(1)
println("B")
latchC.countDown()
}
}.start()
Thread {
repeat(10) {
latchC.await()
latchC = CountDownLatch(1)
println("C")
latchA.countDown()
}
}.start()
latchA.countDown()
}

CyclicBarrier

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import java.util.concurrent.CyclicBarrier

fun main() {
val cb1 = CyclicBarrier(2)
val cb2 = CyclicBarrier(2)
val cb3 = CyclicBarrier(2)
Thread {
repeat(10) {
cb3.await()
println("A")
cb1.await()
}
}.start()
Thread {
repeat(10) {
cb1.await()
println("B")
cb2.await()
}
}.start()
Thread {
repeat(10) {
cb2.await()
println("C")
cb3.await()
}
}.start()
cb3.await()
}

无锁自旋

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
private const val STATE_A = 1
private const val STATE_B = 2
private const val STATE_C = 3

@Volatile
private var state = STATE_A

fun main() {
Thread {
repeat(10) {
while (state != STATE_A) {
}
println("A")
state = STATE_B
}
}.start()
Thread {
repeat(10) {
while (state != STATE_B) {
}
println("B")
state = STATE_C
}
}.start()
Thread {
repeat(10) {
while (state != STATE_C) {
}
println("C")
state = STATE_A
}
}.start()
}

题目

LeetCode.25.K 个一组翻转链表(困难)

给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。

k 是一个正整数,它的值小于或等于链表的长度。

如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。

进阶:

你可以设计一个只使用常数额外空间的算法来解决此问题吗?
你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。

阅读全文 »

VirtualApk跟其他的插件化框架有何区别?

官方介绍

  • 支持几乎所有的Android特性,支持加载加载四大组件。
  • 兼容性好
    • 兼容市面上几乎所有的Android手机,这一点已经在滴滴出行客户端中得到验证;
    • 资源方面适配小米、Vivo、Nubia等,对未知机型采用自适应适配方案;
    • 极少的Binder Hook,目前仅仅hook了两个Binder:AMSIContentProvider,hook过程做了充分的兼容性适配;
    • 插件运行逻辑和宿主隔离,确保框架的任何问题都不会影响宿主的正常运行。
  • 入侵性极低
    • 插件开发等同于原生开发,四大组件无需继承特定的基类;
    • 精简的插件包,插件可以依赖宿主中的代码和资源,也可以不依赖;
    • 插件的构建过程简单,通过Gradle插件来完成插件的构建,整个过程对开发者透明。
阅读全文 »

题目

LeetCode.135.分发糖果(困难)

老师想给孩子们分发糖果,有 N 个孩子站成了一条直线,老师会根据每个孩子的表现,预先给他们评分。

你需要按照以下要求,帮助老师给这些孩子分发糖果:

每个孩子至少分配到 1 个糖果。
评分更高的孩子必须比他两侧的邻位孩子获得更多的糖果。
那么这样下来,老师至少需要准备多少颗糖果呢?

阅读全文 »

什么是热修复?有什么用?

让应用能够在无需重新安装的情况实现更新,帮助应用快速建立动态修复能力。

热修复的使用场景在哪?有什么用?

轻量而快速的升级

热修复是一个动态修改代码与资源的方式,适合于修改量较少的情况。

传统的APP发布升级流程慢,热补丁可以在更短的时间内发布,非常适合在灰度阶段快速验证问题是否已修复,大大缩短发布流程。

热补丁技术可以降低开发成本,缩短开发周期,实现轻量而快速的升级。

远端调试

排查用户反馈的问题会遇到”本地不复现”,”日志查不出”,”联系用户不鸟你”等情况,只要能向特定用户发送补丁,就可以很方便的排查问题,不用依赖用户操作,可以默默的就排查解决。

数据统计

可以方便的进行ABTest,对不同的用户群发送不同的补丁,做不同的行为统计

阅读全文 »