文章目录
jdk、jre、jvm
- 从大到小的包含关系
==和equals区别
- == 比较的时栈中的值,基本数据类型是变量值,引用类型则是堆中内存对象的地址
- equals默认和==一样,会重写,string会重写,
- string本身是final static的,每个对象==是不同的,但是equals就会返回true
final
- 修饰类:不能被继承
- 修饰方法:不能被子类覆盖
- 修饰变量:不能更改值
- 类变量,只能在静态初始代码块中赋值
- 成员变量,可以在非静态初始化赋值,但是必须在引用之前赋值
- 修饰局部变量,只能在局部函数或匿名类使用:因为保证局部函数运行时的正确性,实际访问的是局部变量的copy
- 修饰基本类型不能更改,修饰引用变量不能修改引用,但是可以修改所引用的值 如final int[] a = {1,2,3,4} 可以修改a[1] = 3
string、stringbuffer、stringBuilder
- string是final修饰的,每次操作产生新的string对象
- stringBuffer和stringBuilder都是在原对象上操作
- stringBuffer是线程安全,有synchronized修饰,而stringBuilder则不是线程安全
- 性能stringbuilder>stringbuffer>string
重载和重写
- 重载:发生在同一个类中,方法名不同,参数类型不同,个数不同,顺序不同都是重载,但是返回值不同不能当作重载,存在两个函数名参数相同,返回值不同会直接编译错误
- 重写:发生在父类中,参数列表和方法名必须相同,返回值返回小于等于父类,抛出的异常小于等于父类,修饰符范围大于等于父类
抽象类和接口
- 抽象类存在普通成员函数,接口只能存在public abstract()方法
- 抽象类中的成员变量可以是各种类型的,接口中的成员变量只能是public static final类型的
- 抽象类接口只能继承一个,接口可以实现多个
- 接口是对行为的约束,抽象类是对共性的抽取
- 抽象类关注类的本身共性,接口关注行为动作
- 抽象类单继承,接口可以多继承
list和set
- list:有序,可重复,内存连续存储,可以下标或者迭代取出
- set:无序,不可重复,内存随机存储,只能迭代取出
hashCode和equals
- 两个对象相等,hashcode一定相等,equals也相等
- 相同的hashcode可能两个对象不一样(散列冲突)
- hashcode的值是散列的下表,如果没有重写hashcode,不可能存在两个一样的hashcode
arraylist和linkedlist
- arraylist:基于动态数组,内存连续,适合下表访问,尾部添加代价小,头部添加代价高
- linkedlist:基于链表,可以分散存储于内存中,适合插入删除,不适合查询,查询需要遍历一遍
hashmap和hashtable
- hashtable:线程安全
- hashmap:数组加链表
- 链表高度达到8,数组长度超过64,则转变为红黑树,小于6则重写变回链表
- 计算key的hash值,然后二次hash对数组长度取模,对应到数组,没有产生冲突,直接存入。
- 产生冲突equals比较,相同则取代,不同则插入链表
- key为null,存储在下标为0的位置
- 数组扩容和动态数组扩容一样
concurrenthashMap
- 采用分段锁机制
- synchronized+cas+node+红黑树,node和val都是用volatile修饰
- volatile的作用是让并发时写操作强制在读操作前
- 查找、替换。赋值都是用cas
- 扩容使用synchronized
- 第一次hash找到对应的segment,第二次hash找到对应的链表头
- 对数据的扩容都是以segment为粒度,更加的细微,写入修改的加锁是对头节点。读取不加锁,但是有volatile机制
IOC
- 配置文件扫描路径
- 递归包扫描获取.class文件
- 反射、确定交给IOC管理的类
- 对需要注入的类进行依赖注入
java类加载器
- booststrapClassloader:lib下的jar包和class文件
- extClassloader:lib/ext包下的jar包和class类
- appClassloader:自定义类加载器,负责加载classpath文件
双亲委派
-
从下往上委派,从上往上查找缓存是否加载,找到之后就加载当前的类,所以同名类都是加载最高层
-
从上往下加载:如果没找到,则从上往下加载,在哪个加载器找到就加载哪个加载器的class
-
好处,安全,防止用户自己编写类覆盖核心源码
-
避免重复加载
java中异常
- 顶层throwable
- execption和error
- checkexection和runtimeException
gc如何判断对象可以回收
- 引用数为0
- 可达性分析
线程的声明周期
- 创建、就绪、运行、阻塞、死亡
- 等待阻塞,运行中的线程执行wait方法,线程会释放所有资源,jvm把线程放入等待池中,开始等待,不能自动唤醒,需要其他线程调用notify或者notifyall方法。
- 同步阻塞,没有抢到锁,会放入锁池中
- 其他阻塞,sleep方法或者join方法(在B线程中调用A.join则会在A执行之后再执行B)
- 新建状态:新创建一个线程
- 就绪状态:线程对象创建之后,调用start()方法,处于可运行线程池中等待cpu调度
- 运行:cpu正在运行线程
- 阻塞状态,放弃cpu使用权,暂停运行,知道就绪状态
- 死亡:执行完成,结束生命周期
sleep/wait/join/yield
- 锁池
- 所有需要竞争同步锁的线程都会放在锁池中,得到锁后进入就绪态
- 等待池
- 待用wait()方法后,线程静入等待池,等待池线程不会竞争同步锁,只有调用notify才会被唤醒加入锁池竞争锁,notify是随机唤醒一个等待池,notifyall唤醒所有
- sleep是thread类方法,wait是object类的本地方法
- sleep不会释放锁,wai会释放锁
- sleep不依赖同步器syn,wait需要
- sleep不需要唤醒,wait需要
- sleep一般用于当前线程休眠或者循环暂停,wait主要用于多线程通信
- sleep会让出cpu执行时间强制上下文切换,wait不一定,wait之后有机会重新竞争到锁
- yield执行后线程直接进入就绪状态,马上释放cpu执行权,但是依然保留了cpu的执行资格,可能下一个就会执行
- join执行后线程a会等待线程b执行完或者中断
thread和runable
- thread类,实现了runable
- runable接口
守护线程
- 为非守护线程提供服务的线程
- 任意一个守护线程都是jvm中非守护线程的保姆
- 不能拥有固定资源
- 系统线程设置为守护线程也会被转成用户线程
ThreadLocal的原理
-
thread包含一个threadlocalmap类型的成员变量threadlocal存储本线程中所有threadloca对象及对应的值
-
线程间相互独立,说明线程安全
-
每个threadlocalMap里面存储entry对象,key是弱引用的threadlocal对象,值是线程变量。可由set、get操作
- 对象跨层传递时,可以避免多次传递,使用同一个threadlocalmap
- 线程间数据给力
- 事务操作,存储线程事务信息
- 数据库连接,Session会话管理
threadlocal内存泄漏及避免
- 不再被使用的对象或者变量占用的内存不能被回收,就是内存泄漏会导致内存溢出
- 强引用(new,反射),一个对象具有强引用,不会被垃圾回收器回收,如果想强制取消关联,可以显示应用赋值为null
- 弱引用,总是被gc回收,常在缓存中使用
- key是弱引用,被回收会存在值无法被回收的情况
- 但是当被回收后调用THreadLoaclMap的set()get()remove()时,就会消除key为null的value值
- 每次使用threadlocal都要调用remove()方法进行清楚数据
- 将thread local定义成private static。这样就一直存在threadlocal的强引用,这样就能通过threadlocal的弱应用访问到entry的value值
并发三大特性
- 原子性
- 可见性
- 有序性
线程池和线程池参数
- 降低资源消耗,提高线程的利用率,降低创建和销毁的消耗
- 提高响应度
- 提高线程的可管理性
- corepoolsize代表核心线程数,创建后一般不会消除
- maxinumPoolSize代表最大线程数,与核心线程数对应
- keepAliveTime,unit表示超出核心线程数之外的线程的空闲存活时间
- workQueue用来存放待执行的任务
- threadFactory线程工厂
- handler任务拒绝策略,两种情况,调用shutdown等方法结束时,线程池内部有还没有执行完成的任务,但线程池关闭,所以会拒绝,还有就是到达最大线程数,无法处理新提交的任务
线程池流程
- 添加任务-
- 检查核心线程是否满 no 创建核心线程
- 任务队列是否满 no 添加到任务队列
- 最大线程是否达到 no 创建临时线程
- 拒绝策略
线程池阻塞队列
- 达到最大队列后,可以通过阻塞保留需要入队的线程
- 自带休眠唤醒
- 创建新线程时,需要获取全局锁,其他线程都得阻塞
线程池复用
- 每个线程都会重复执行任务,调用run方法。
- 线程和任务分离,线程池不包含逻辑,run方法里面拥有逻辑