请你说一下抽象类和接口的区别
构造方法:抽象类有,接口没有
方法:抽象类有,接口有抽象方法,default默认实现的方法
变量:接口只有public static final
修饰的静态变量
修饰符:抽象类有public private protected
,接口有public
,default
抽象类可以被继承,接口可以被实现,一个类只能继承一个抽象类,实现多个接口
作用:抽象类作用类似模板,有很多子类来继承它,模板即实现了部分功能,搭好了部分,剩下的由不同子类来实现,即不同种方式;接口作用类似多个程序之间的通信标准
请你说说==与equals()的区别
两者都是用来判断两个变量是否相等
==
:如果两个变量都是基本数据类型,而且都是数值类型的,两个变量值相等就返回true
,如果两个变量是引用的话,比较是引用的地址是否一样
equals
:如果没有重写父类object
的equals()
,那么它和==
使用没有区别,所以我们需要根据业务来重写equals()
方法,大部分重写后用来比较对象的内容是否相等
说说synchronize的用法及原理
四种锁状态;无锁、偏向锁、轻量级锁、重量级锁,由轻到重
用法:synchronize
可以作用在静态方法、普通方法、代码块上
- 静态方法:锁的是当前类
class
- 普通方法:锁的实例
this
- 代码块:需要一个对象作为参数,锁定指定参数的对象
原理:对象主要由三部分组成,对象头、对象实例数据、对齐填充
- 对象头主要由
Mark Word
、class metadata address
(确定这个对象属于哪个类)、arraylength
组成; MarkWord
里有锁信息- 每个锁都有一个
monitor
对象;monitor
主要由objectmonitor
实现,objectmonitor
主要属性有owner、entrylist、waitset
;owner
表示哪个线程持有锁,entrylist
等待锁的线程,waitset
阻塞的线程 - 方法和代码块同步都是依赖
monitor
对象,代码块使用monitorenter
和monitorexit
指令,方法使用flag
标记acc-synchronize
为什么需要锁升级,重量级锁依赖monitor
,monitor
依赖mutex lock
,所以重量级锁又称为互斥锁,重量级锁会把除了拥有锁的以外线程阻塞,唤醒线程需要操作系统来帮忙,需要用户态切换为内核态,切换成本较高,对性能有很大的影响,JDK6
的时候引入偏向锁和轻量级锁
- 偏向锁:偏向某个线程的锁,如果只有一个线程竞争锁,防止多次获取锁,主要实现原理是通过
CAS
把当前线程ID
记录到MarkWord
的偏向线程ID
中,下次获取锁时,判断当前线程ID
和偏向ID
是否一致,一致的话认为当前线程已经获取锁;不一致的话,则把偏向锁状态撤销,转变为轻量级锁 - 轻量级锁:将对象头中的
MarkWord
信息复制到栈帧的锁记录(lock record
)中,然后使用CAS
尝试将对象头中MarkWord
替换成指向锁记录的指针;如果失败,则说明有其他线程竞争锁,尝试不断自旋获取锁,当到达一定自旋次数后还未获取锁,则把轻量级锁转变为重量级锁
说说你对AQS的理解
AQS
:AbstractQueueSynchronize
,抽象队列同步器,Lock
实现类都是基于AQS
实现,AQS
主要的两个内部类Node
、ConditionObject
,其中Node
是等待的线程节点,线程被阻塞,封装线程为Node
放入等待队列,ConditionObject
,顾名思义条件对象,提供类似wait/notify
功能,唤醒线程
AQS
的核心是state
同步状态和FIFO
等待队列,state
由volatile
修饰保证同步状态线程安全,state=0
表示可以获取锁,state>=1
表示锁已被使用,先进先出的等待队列放被阻塞的线程,等锁释放后,就会取出头线程(公平锁方式)
Java哪些地方使用了CAS
JUC
包下的类
说说JVM的垃圾回收算法
标记-清除:标记所有存活的对象,然后删除所有未标记的,缺点内存碎片多,导致分配较大的对象时无法找到足够的连续内存
标记-整理:标记所有存活的对象,然后整理到一边,清除所有未标记的,缺点效率低下
标记-复制:把内存划分为两个区域,其中一个区域存活的对象复制到另一个区域,清除未存活的,然后又复制回去;优化,将内存区域划分为Eden
和两个survivor
区,比例8:1:1
,垃圾回收时将Eden
和其中一个survivor
还存活的对象复制到另一个survivor
区,这样新生代可用的内存空间为整个新生代容量的90%