more
注释之前的内容被视为文章摘要。

Android中的消息机制主要指Handler的运行机制。Handler的使用过程很简单,通过它可以轻松地 将一个任务切换到Handler所在的线程中去执行 。Handler在日常开发中的最常用的作用是通过它更新UI。具体来说是这样的:有时候需要在子线程中进行耗时的IO操作,可能是读取文件或者访问网络等,当耗时操作完成以后可能需要在UI上做一些改变,由于Android开发规范的限制,我们并不能在子线程中访问UI控件,否则就会触发程序异常,这个时候通过Handler就可以将更新UI的操作切换到主线程中执行。因此,本质上来说,Handler并不是专门用于更新UI的,它只是常被开发者用来更新UI。
ThreadLocal是通过线程隔离的方式防止任务在共享资源上产生冲突, 线程本地存储是一种自动化机制,可以为使用相同变量的每个不同线程都创建不同的存储。 @pdai
带着BAT大厂的面试问题去理解
- 什么是ThreadLocal? 用来解决什么问题的?
- 说说你对ThreadLocal的理解
- ThreadLocal是如何实现线程隔离的?
- 为什么ThreadLocal会造成内存泄露? 如何解决
- 还有哪些使用ThreadLocal的应用场景?
前言
对于ThreadLocal
,大家的第一反应可能是很简单呀,线程的变量副本,每个线程隔离。那这里有几个问题大家可以思考一下:
ThreadLocal
的 key 是弱引用,那么在ThreadLocal.get()
的时候,发生GC之后,key 是否为null?ThreadLocal
中ThreadLocalMap
的数据结构?ThreadLocalMap
的Hash 算法?ThreadLocalMap
中Hash 冲突如何解决?ThreadLocalMap
的扩容机制?ThreadLocalMap
中过期 key 的清理机制?探测式清理和启发式清理流程?ThreadLocalMap.set()
方法实现原理?ThreadLocalMap.get()
方法实现原理?- 项目中
ThreadLocal
使用情况?遇到的坑? - ……
很多(我就直接代表很多人了🐶)做 Android 的同学认识和学习 ThreadLocal 都是通过 Looper 中的 sThreadLocal 这个静态变量开始的,然后就会进入一个误区:sThreadLocal 是 Looper 中的一个静态变量啊,当
Looper#prepare(boolean quitAllowed)
方法调用sThreadLocal.set(new Looper(quitAllowed))
的时候存到 ThreadLocalMap 中的 value (Looper) 的 key 值竟然是 this,也就是 sThreadLocal 这个静态变量?那一个静态变量怎么能作为 key值呢?静态变量在这个进程中可是独一份啊,其他线程的 Looper 再调用 Looper#prepare(boolean quitAllowed) 的时候岂不会把之前的 Looper 给替代了?
本节将介绍 View 的一个核心知识点:事件分发机制
。
事件分发机制不仅仅是核心知识点更是难点,不少初学者甚至中级开发者面对这个问题时都会觉得困惑。另外,View 的另一大难题滑动冲突
,它的解决方法的理论基础就是事件分发机制,因此掌握好 View 的事件分发机制是十分重要的。本节将深入介绍 View 的事件分发机制,在这一篇会对事件分发机制进行概括性地介绍,而在下一篇将结合系统源码去进一步分析事件分发机制。
一、点击事件的传递规则
在介绍点击事件的传递规则之前,首先我们要明白这里要分析的对象就是 MotionEvent
,即点击事件
,关于 MotionEvent 在之前已经进行了介绍。所谓点击事件的事件分发,其实就是对 MotionEvent 事件的分发过程,即当一个 MotionEvent 产生了以后,系统需要把这个事件传递给一个具体的 View,而这个传递的过程就是分发过程。点击事件的分发过程由三个很重要的方法来共同完成:dispatchTouchEvent
、onInterceptTouchEvent
和 onTouchEvent
,下面我们先介绍一下这几个方法。
final 关键字看上去简单,但是真正深入理解的人可以说少之又少,读完本文你就知道我在说什么了。本文将常规的用法简化,提出一些用法和深入的思考。@pdai
带着BAT大厂的面试问题去理解final
提示
请带着这些问题继续后文,会很大程度上帮助你更好的理解final。@pdai
- 所有的final修饰的字段都是编译期常量吗?
- 如何理解private所修饰的方法是隐式的final?
- 说说final类型的类如何拓展? 比如String是final类型,我们想写个MyString复用所有String中方法,同时增加一个新的toMyString()的方法,应该如何做?
- final方法可以被重载吗? 可以
- 父类的final方法能不能够被子类重写? 不可以
- 说说final域重排序规则?
- 说说final的原理?
- 使用 final 的限制条件和局限性?
- 看本文最后的一个思考题
前言
单例模式在Java开发中是非常经典和实用的一种设计模式,在JDK的内部包的好多api都采用了单例模式,如我们熟悉的Runtime类.
单例模式总的来说有两种创建方式,一种是延迟加载的模式,一种是非延迟加载的模式,今天我们来学习一下基于双检锁延迟加载的单例模式。
什么是单例模式
顾名思义,单例模式指的是在整个程序运行期间,我们只能初始化某个类一次,然后一直使用这个实例,尤其是在多线程的环境下,也要保证如此。
基于双检锁的单例模式
在介绍基于双检锁的单例模式下,我们先思考下如何在使用延迟加载的情况下实现一个单例模式,可能有一些比较年轻的小伙伴,不假思索的就写下了下面的一段代码:
内存屏障是硬件之上、操作系统或JVM之下,对并发作出的最后一层支持。再向下是是硬件提供的支持;向上是操作系统或JVM对内存屏障作出的各种封装。内存屏障是一种标准,各厂商可能采用不同的实现。
本文仅为了帮助理解JVM提供的并发机制。首先,从volatile的语义引出可见性与重排序问题;接下来,阐述问题的产生原理,了解为什么需要内存屏障;然后,浅谈内存屏障的标准、厂商对内存屏障的支持,并以volatile为例讨论内存屏障如何解决这些问题;最后,补充介绍JVM在内存屏障之上作出的几个封装。为了帮助理解,会简要讨论硬件架构层面的一些基本原理(特别是CPU架构),但不会深入实现机制。
在 C 程序代码中我们可以利用操作系统提供的互斥锁来实现同步块的互斥访问及线程的阻塞及唤醒等工作。在 Java 中除了提供 Lock API 外还在语法层面上提供了 synchronized 关键字来实现互斥同步原语, 本文将对 synchronized 关键字详细分析。
带着 BAT 大厂的面试问题去理解 synchronized
- synchronized 可以作用在哪里? 分别通过
对象锁
和类锁
进行举例。 - synchronized 本质上是通过什么保证线程安全的? 分三个方面回答:加锁和释放锁的原理,可重入原理,保证可见性原理。
- synchronized 有什么样的缺陷? Java Lock 是怎么弥补这些缺陷的。
- synchronized 和 Lock 的对比,和选择?
- synchronized 在使用时有何注意事项?
- synchronized 修饰的方法在抛出异常时,会释放锁吗?
synchronized 修饰的方法,无论方法正常执行完毕还是抛出异常,都会释放锁
- 多个线程等待同一个 synchronized 锁的时候,JVM 如何选择下一个获取锁的线程?
新来的线程有可能立即获得监视器,而在等待区中等候已久的线程可能再次等待,这样有利于提高性能,但是也可能会导致饥饿现象。
- synchronized 使得同时只有一个线程可以执行,性能比较差,有什么提升的方法?
- 我想更加灵活的控制锁的释放和获取(现在释放锁和获取锁的时机都被规定死了),怎么办?
- 什么是锁的升级和降级? 什么是 JVM 里的偏斜锁、轻量级锁、重量级锁?
- 不同的 JDK 中对 synchronized 有何优化?