一、lock、synchronized 和 volatile的区别
synchronzied 同步代码块底层原理 : monitorenter指向同步代码块的开始位置,monitorexit指向同步代码块的结束位置。线程执行到monitorenter指令时,该线程对象持有锁,即线程获得monitor持有权,count计数器+1,获得线程锁,线程执行完毕后,执行monitorexit执行指令,count为0,释放锁。 注意:无论是正常结束还是异常结束,都会执行monitorexit指令
synchronized 的同步方法的底层原理 : ACC_SYNCHRONIZED的flag标记该方法是否是同步方法,从而执行相应的同步调用
synchronized 和 volatile 的区别
volatile只能在线程内存和主内存之间存一个变量值,synchronized可以修饰类、对象、方法等,但是synchronized很消耗内存
volatile的理解:具有可见性、有序性,不具备原子性、禁止指令重排。 volatile修饰的变量会强制把数值写入主内存中
synchronized 和 lock 的区别
synchronized是Java的关键字,Lock是接口
synchronized是自动释放锁,Lock需要手动释放锁,所以写到try catch中并且在finally中释放锁
synchronized无法中断等待的锁,Lock可以中断,它可以在激烈的读写操作
synchromized比Lock性能好
Java内存模型:原子性、可见性、指令重排序
Java内存结构:堆、栈、方法区、常量区、静态区、程序计数器
二、volatile的原理讲解
原理:从工作线程内存到主内通过以上数据原子操作指令来完成,它会在底层汇编前面加上lock这样关键字
lock关键字作用:
可以直接让处理器缓存区的数据直接写回到主内里面
也提供内存屏障,防止指令重排序
三、创建线程的方法
1.继承thread方法
Thread start 和 run 的方法
start : 开启每一个线程的启动方法,一个线程只能开启一次,再开启就会报线程异常
run : 只是执行方法,可以多次执行,不能起到多线程作用
2.实现Runnable接口
3.实现CallBack接口
4.创建线程池
5.使用定时器
四、sleep和wait(notify)区别
sleep是Thread的内部的一个静态方法,可以再任何地方调用;wait是Object成员方法,只能在synchroized代码中被调用,其他地方调用会非法监控异常
在等待时候,wait是释放锁的,用于线程交互;sleep会一直持有锁,不会改变锁的状态,用于线程切换cpu的状态
五、线程池的使用
五种线程池、四种拒绝策略、三种阻塞队列
五种线程池
threadPool = Executors.newCachedThreadPool(); //有缓冲的线程池,线程数 JVM 控制
threadPool = Executors.newFixedThreadPool(3); //固定大小的线程池,支持线程池并发
threadPool = Executors.newScheduledThreadPool(2); //固定大小的线程池,支持定时或者周期执行任务
threadPool = Executors.newSingleThreadExecutor(); //单线程的线程池,只有一个线程在工作
threadPool = new ThreadPoolExecutor(); //默认线程池,可控制参数比较多
四种拒绝策略
rejected = new ThreadPoolExecutor.AbortPolicy(); //默认,队列满了丢任务抛出异常
rejected = new ThreadPoolExecutor.DiscardPolicy(); //队列满了丢任务不异常
rejected = new ThreadPoolExecutor.DiscardOldestPolicy(); //将最早进入队列的任务删,之后再尝试加入队列
rejected = new ThreadPoolExecutor.CallerRunsPolicy(); //如果添加到线程池失败,那么主线程会自己去执行该任务
三种阻塞队列
有限队列
ArrayBlockingQueue<>(5); //基于数组的先进先出队列,有界
无限队列
SynchronousQueue<>(); //无缓冲的等待队列,无界适用于 newCachedThreadPool线程池
LinkedBlockingQueue<>(); //基于链表的先进先出队列,无界适用于newFixedThreadPool、newScheduledThreadPool
默认线程池的参数讲解:
* corePoolSize:核心线程池大小
* maximumPoolSize:最多线程池大小
* keepAliveTime:表示空闲线程的存活时间
* unit:keepAliveTime时间单位
* workQueue:阻塞任务队列
* threadFactory:新建线程工厂
* RejectedExecutionHandler:保护策略
线程池的submit和execute执行方法的区别
1、submit在执行过程中与execute不一样,不会抛出异常而是把异常保存在成员变量中,execute直接抛出异常之后线程就死掉了,submit保存异常线程没有死掉
2.submit有返回值,execute没有返回值
六、锁的概念
重入锁:当前已经拿到类的锁,实际上还是可以再调用有锁的方法
自旋锁: 当前拿到线程锁以后,其他线程不会就如休眠状态
互斥锁:当前线程拿到锁以后,其他线程就会进入休眠状态
读写锁的解释和场景
当某线程拿到读锁的时候,只有具有读锁的线程可以申请读锁,但不能申请写锁
当某线程拿到写锁的时候,只有具有写锁的线程可以申请写锁,但不能申请读锁
拿到申请读锁时候,不一定要读数据,但是申请写锁时候,一定要写数据,适用场景是只有读写锁,且读锁的需求比写锁的需求多
原子类:Atomic基本数据类型。 AtomicIntege(整数加锁)、AtomicBoolean(boolean加锁).......
AQS : ReetracntLock 就是用这个方法去写的
CountDownLatch : 通过wait进行等待,当减为0才会唤醒线程
七、HashTable、HashMap、ConcurrentHashMap、LinkHashMap之间的区别
ConcurrentHashMap的视频
https://www.bilibili.com/video/BV1BC4y187Ti
LinkHashMap和HashMap的区别
LinkHashMap : 是HashMap的一个子类,它存储是有序的,HashMap : 它的存储是无序的。
HashMap和HashTable是底层数组+链表实现
ConcurrentHashMap是由Segment (ReentrantLock可重入锁)数组结构+HashEntry数组结构组成。
HashTable和HashMap的区别:
HashTable无论key还是value都不能为null,线程安全; HashMap可以存储null键和null值,线程不安全
HashMap的扩容是:默认是16,扩容是2*old ,负载因子是0.75; HashTable的扩容是:默认是11,扩容是2*old+1
HashMap的存值
ConCurrentHashMap的存值
https://www.bilibili.com/video/BV1BC4y187Ti?p=18 视频观看
ConcurrentHashMap是HashTable优化版,Hashtable中采用的锁机制是一次锁住整个hash表,从而在同一时刻只能由一个线程对其进行操作;而ConcurrentHashMap只是锁住当前线程的数据,其他线程还是可以访问该hash表
Android 中的SparseArray和HashMap的区别
优势:SparseArray比HashMap更省内存,在某些条件下性能更好,主要是因为它避免了对key的自动装箱(int转为Integer类型),它内部则是通过两个数组来进行数据存储的,一个存储key,另外一个存储value
SparseArray应用场景:
虽说SparseArray性能比较好,但是由于其添加、查找、删除数据都需要先进行一次二分查找,所以在数据量大的情况下性能并不明显,将降低至少50%。
满足下面两个条件我们可以使用SparseArray代替HashMap:
数据量不大,最好在千级以内
key必须为int类型,这中情况下的HashMap可以用SparseArray代替:
十三、JVM虚拟机理解
对JVM理解的网址 : https://blog.jamesdbloom.com/JVMInternals.html
java的内存模型:原子性、可见行、指令重排序
visualGC(JDK里面自带)插件可以检查JVM的内存模型
1.JVM的内存结构
堆区(新生代(1/3)、老生代(2/3))、栈区、方法区、静态区、程序计数器
2.JVM内存分配,新生代的大小可以在AndroidStudio上配置大小
a.大的对象直接分配到老生代
b.先分配到新生代,第一次GC之后会被放在survivor区,多次GC仍存活就会被放到老生代区
c.第一次再往survivor区移动的对象时候,总对象超过它内存的百分之50就会被送到老生代区
d.在GC先对老生代进行判断,是否所有的新生代对象的大小比老生代大,如果大就行一次Full GC的操作
Minor GC 指的是发生在年轻代或者说新生代(Young space)中的gc
Major GC 指的是发生在老年代(Tenured space)中的gc
survivor区的作用是解决了碎片化,不用所有的回收对象都会被放在老生代里面,以防老生代很快填满
3.如何判定对象是垃圾对象
a.引用技术法:有对象引用计数就+1,如果为0就会被回收掉。
缺点是:一旦两个对象变量相会引用,对象销毁,但是还是无法被回收,计数不为0
b.可达性分析法
c.如何判断一个类是否为无用类
堆中不存在该类的任何实例
该类的加载器ClassLoader已经被回收了
4.垃圾回收算法
a.回收策略
标记-清除法 : 把标记的垃圾对象直接清除
缺点:会有很多内存碎片,导致于无法连续使用
标记-整理法 :一边将垃圾对象回收、一遍把存活对象向一端移动,保持内存连续完整性
复制算法 : 将内存一分为二,把可用对象复制到另外一半上,其余一次性清除
缺点:浪费内存的使用
分代收集法 : 这个是JVM主流算法,新生代回收新生代、老生代回收老生代
b.垃圾回收器:它是把回收算法用代码实现,收集器都是可以在AndroidStudio上配置
Serial收集器 : 它是一个单线程的收集器,它在进行垃圾回收,其他线程需要停止工作
ParNew收集器 :是Sercial多核版本的使用,作为新生代使用
Parallel Scavenge垃圾收集器 :适用于内存大于2G和2个CPU设备
CMS垃圾收集器:采用标记-清除算法,只在老年代上使用
优点:并发收集、并发处理、低停顿
缺点:和用户线程抢占资源、采用标记-清除算法会处理对象较耗时且产生不连续的碎片内存、回收的不确定性会产生FullGC
G1垃圾回收器 :采用复制算法 并发标记-->最终标记-->筛选回收
筛选回收机制:先对各个内存大小进行排序,再符合GC停顿时间内,把这些垃圾分段回收
G1垃圾回收器的种类:Minor GC、Major GC、Full GC
九、触发GC的条件
1.当应用程序空闲时,即没有应用线程再运行时,GC会被调用的
2.JAVA内存不足的时候,GC会被调用
十、减少GC的开销措施
1.不要显示调用System.gc()
2.尽量减少创建临时对象(相当于减少垃圾的产生,从而延长第二次GC的触发时间),对象不用时候最好显示设置为null(有利于GC收集器判定垃圾,从而提高垃圾回收)
3.尽量使用StringBuffer,而不用String来增加字符串
4.能使用基本变量就不用封装好的对象(比如int ---> Integer对象)
5.尽量减少使用静态变量(静态变量属于全变量,不会被GC回收掉,他们也会一直占用内存,增加GC的次数)
十三、Java的设计六原则
单一原则、开闭原则、里氏替换、依赖倒置、接口抽离、迪米特法则
十四、设计模式
装饰者和适配器模式的区别
1.装饰者模式:给对象添加功能,可以动态生成和者取消 案例:FilterInputStream既包含InputStream和BufferInputStream两种流
2.适配器模式:将一个接口转变成另一个接口,通过改变接口来达到重复使用的目的
案例:InputStreamReader是有Reader接口和InputStream接口组成一个新的接口
外观模式和代理模式的区别
3.外观模式:把复杂的方法合并成一个外观方法供外面使用,提供调用简单,侧重于方法
4.代理模式:把一个功能封装在一个对象里面,然后供外界使用,侧重于是对象
建造者模式和工厂模式的区别
5.建造者模式:适用于复杂参数初始化,可以通过建造者模式来构建,侧重于方法
6.工厂模式:创建复杂且大量的对象来构建,侧重于对象
单例模式和共享元模式的区别
7.单例模式:它是针对于有且只有一个对象,侧重于对象
8.共享元模式:它是针对于一个共享数据来做处理,侧重于整体数据。例Handler的MessageQueue存储Message
9.责任链模式
发布订阅模式和观察者模式区别
10.观察者模式 : 数据源直接通知订阅者发生改变 ,他比较关注一个关心数据源
11.发布订阅模式 :数据源告诉第三方(事件频道)发生了改变,第三方再通知订阅者发生了改变,他比较关注事件消息
12.桥接模式
13.组合模式
14.策略模式
十五、范型的上下通配符
? ----- > 通配符 : java里面支持类的继承,而泛型不支持类的继承,? 代表所有类的父类
extends---> 通配符上边界 : 只接受本身和自己的子类
super---->通配下边界 : 只接受本身和自己的父类
? extends T : 只支持本身和自己的子类的任何类型
? super T : 只支持本身和自己的父类的任何类型
上边界和下边界什么时候使用:
上边界:在读取T的数据,但是不写入时候
下边界:在写入T的数据,但是不读取时候
范型擦除:定义<T>这样的方法,编译器会把T认为是Object对象,再编译期间需要时候再强制转型
十六、内部类为什么可以调用外部类的成员变量
在调用内部类的构造器初始化内部类对象的时候, 编译器默认也传入外部类的引用给内部类,这样就可以访问到外部类的成员和方法了,内部类也可以访问外部类的私有成员,最终通过编译器写成static形式来去访问
十七、TCP和UDP的区别
a.基于连接与无连接
b.对系统资源的要求(TCP较多,UDP少)
c.UDP程序结构较简单
d.流模式与数据报模式
e.TCP保证数据正确性,UDP可能丢包,TCP保证数据顺序,UDP不保证。 UDP (应用场景 IP电话,实时视频会议)
十八、三次握手和四次挥手的意义
三次握手的目的是建立可靠的通信信道,主要的目的就是双方确认自己与对方的发送与接收机能正常
四次挥手是表示自己发完了所有要发的数据,但还允许对方继续把没发完的数据发过来,确保数据都能完整发送
TCP还设有一个保活计时器,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75秒钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。
十九、GET请求和POST请求的区别
1. 大小是否有限制:GET请求有限制;POST请求没有
2.安全性:GET请求提交的数据都在URL中,可见,不安全;相对来说POST请求更加安全
3.JS中获取参数上的区别:GET请求在JS中是通过Request.QueryString["xxx"]获取URL中的参数,而POST请求在JS中是通过Request.Form["xxx"]获取Form表单中的参数
十九、http1.0、1.1和2.0的区别
http1.0和http1.1的主要区别如下:
1、缓存处理:1.1添加更多的缓存控制策略(如:Entity tag,If-Match)
2、网络连接的优化:1.1支持断点续传
3、错误状态码的增多:1.1新增了24个错误状态响应码,丰富的错误码更加明确各个状态
4、Host头处理:支持Host头域,不在以IP为请求方标志
5、长连接:减少了建立和关闭连接的消耗和延迟。
http1.1和http2.0的主要区别:
1、新的传输格式:2.0使用二进制格式,1.0依然使用基于文本格式
2、多路复用:连接共享,不同的request可以使用同一个连接传输(最后根据每个request上的id号组合成正常的请求)
3、header压缩:由于1.X中header带有大量的信息,并且得重复传输,2.0使用encoder来减少需要传输的hearder大小
4、服务端推送:同google的SPDUY(1.0的一种升级)一样
二十、JDK1.8的特性
Lambda表达式
函数式接口 -----> interface
方法引用和构造器调用 ::
Stream API
新时间日期API
HashMap存储从头插入变为尾插入
https://space.bilibili.com/627881031?spm_id_from=333.788.b_765f7570696e666f.1
https://cs.android.com/android/platform/superproject;l=417 Android的原生文档
七、throw和throws的区别
throw : 用在方法体内,跟的是对象名,throw抛出异常,一定会抛出某种异常
throws : 用在方法后面,跟的是类名,它抛出的异常只是一种可能性,并不一定会抛出异常的
八、final、finalize、finally的关键字的区别
final : 可以修饰类,修饰的类不能被继承 ; 可以修饰变量,该变量的数值就不能修改
finally : try ... catch 语句中使用
finalize : 在垃圾回收时候使用,被清理对象没有被引用的情况下调用的。
九、Error和Exception的区别
error和exception都是继承Throwable。
Exception: 分为检查异常(CheckException)和非检查异常(RuntimeException),一般是代码代码错误引起,可以预料的意外情况,通过捕获并处理,以使程序重回正轨
Error: 是Java运行时抛出的JVM层面的错误,不能捕捉,发生后只能安全的退出应用