java复习

HashMap 底层数据结构、hash值的优化、寻址的优化

hashMap 底层是由数组 + 链表组成的,jdk7以后是数组 + 链表或者红黑树组成的 当链表长度超过8是会自动将链表转成红黑树(链表的时间复杂度o(n),红黑树的时间复杂度o(logn))
hashMap 的hash算法优化原理 将hash值的二进制数高低位做异或运算。
hashMap 的hash寻址算法优化原理 由原来的取模改成 数组长度减1的差&优化过的hashCode。因为&运算要比取模运算要速度要快的多。只要数组保持2的n次方那么取模运算与&运算效果是一样的。
为什么要进行对原始hashCode 算法做优化。要结合寻址算法来理解。首先优化了对hashCode取模改成&运算。其次基于这个&运算,因为n-1 这个二进制的值不会非常大 如果采用n-1 & 优化过的hash值那么这个相当于高16位的&运算不直接参与。主要在低16位上那么计算出来的int值很大情况下会相同。

HashMap 如何解决hash冲突的

因为hashMap 底层就是数组+链表。 每个数组上面的元素是链表单向的,如果产生了hash冲突后会将这个值放到链表中。如果链表太长会转化成红黑树。

hashMap是如何进行扩容的

当hashMap的长度不够了会自动扩容扩容长度是当前长度的两倍一般是 16 -> 32 ->64 如果长度不够就执行扩容,重新计算数据存放的位置如果计算出与原来的二进制数一样就不移动,如果多一个bit 就把原来容器的长度+原来的寻址位置生成新的位置。

说说synchronized关键字的底层原理是什么(基础)

synchronized 工作原理,两个线程同时执行一个被synchronized修饰的方法时jvm 内部会有一个monitor, 和一个计数器。 执行方法的时候会执行 monitorEnter, 同时会把计数器+1 方法执行完后执行monitorExit 计数器-1。 直到计数器为0 才能允许下一个线程执行这个方法。

CAS的理解以及其底层实现原理

多个线程并发的执行一个共享的变量会导致数据不安全,通常我们会用synchronized关键字修饰这个方法, 但是这个方法会串行化执行影响效率,还有一种是CAS机制。java 并发包中要有一个原子类它就是实现了CAS 机制。 当多线程访问一个共享变量时先尝试将数据读取出来,操作完数据之后设值之前将当前数据与之前读取出来的值作比较如果相同设值成功,不相同设值失败。线程会重新操作。底层的硬件机制会保证同一时刻,只有一个线程进行操作

ConcurrentHashMap底层实现原理

1.8以前 ConcurrentHashMap 和hashMap 一样是采用数组+链表的方式来做的。1.8以后版本是数组+链表或者红黑树来组成。
1.8之前采用分段锁来保证是线程安全的。将一个大数组拆成多个小数组,每个小数组上进行synchronized 加锁。
1.8之后采用一个大数组+cas+synchronized 加锁每一个元素。
当多个线程同时对ConcurrentHashMap 进行写入操作。先不加锁先通过cas机制来写入数据。此时假设一个线程已经修改了数据。其他线程就对这个位置cas失败,在这个元素上加锁。串行化的执行对元素中的操作。这样别的线程进来如果不是针对同一个位置上的元素做操作是可以并发的执行不需要等待上个线程的释放。

AQS实现原理

AQS 是一个组件。其中主要的组成部分是一个包含当前状态,一个存放当前线程,一个线程等待队列。常见的实现类有
reentranlock。
工作原理:当多个线程操作一个代码块的时候底层通过cas将当前状态更改成1。 同时将当前线程保存在这个组件中。
其他线程在会cas失败后加入到组件中的等待队列进行等待。当上个线程任务完成后将状态改成0,清空当前线程,同时唤醒在队列中等待的线程。

线程池的工作原理

在一个系统中不可能无限制的创建线程、销毁线程。因为这样非常的消耗资源。一般情况会构建一个线程池。线程池中有一定数量的线程用来并发的执行任务。
线程池中主要有两个核心组件。1:线程 2 :任务队列
假设创建完线程池后设定线程数为3,此时创建多线程任务会比较池子中的线程是否大于设定数量,如果没有就创建一个线程执行任务。依次类推直到创建第四个任务时发现线程池中只能有3个,那就不创建转而添加到任务队列中等待线程执行,如果此时3个线程都处于繁忙状态那么就会阻塞等待,直到线程任务执行完后会去执行任务队列中的任务,反之线程会立即感知到任务而马上执行。

线程池的参数有什么用

ThreadPoolExcutor 参数有什么作用
corePoolSize : 创建线程默认的核心线程数
maixMumPoolSize : 最大创建的线程数
keepAliveTime : 队列中无任务时额外线程存活时间
queue : 任务队列 可以是无界的,也可以是有界的

当任务是有界队列时假设长度只有200 此时corePoolSize 任务繁忙不能及时处理队列中的任务。那么 我们设置的maixMumPoolSize 比 corePoolSize 大那么会继续帮我们创建临时线程帮我们处理这些任务。
如果那些额外的线程+核心线程还是来不及处理队列中的任务。此时采用reject 机制。
进过一段时间的后任务逐步被消化。队列中没有任务 那经过keepAliveTime 设置的值后然后自动销毁额外的线程

spring 事务传播的几种方式的自我理解

1、PROPAGATION_REQUIRED : 如果当前没有事务就开启事务,如果当前有事务就加入到当前的事务中。spring 默认开启级别
2、PROPAGATION_SUPPORTS : 如果当前没有事务就按照没有事务运行,如果当前有事务就加入到当前事务。一般用在查询语句使用
3、PROPAGATION_MANDATORY : 如果当前没有事务,就报错强制必须要有事务
举例
public void a(){

}

public void b(){

}
如果a方法调用b方法时 a方法中没有事务那么b方法会报错
4、PROPAGATION_REQUIRES_NEW : 新开启一个事务,并挂起当前事务
举例
@Transactional(propagation = Propagation.REQUIRED)
public void a(){

}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void b(){

}
当a方法调用b方法时a和b都是独立的事务互不影响。即两个方法都不在同一个事物中。
a方法发生异常后回滚不会影响b事务同样的,b方法发生异常后回滚不会影响a事务
5、PROPAGATION_NOT_SUPPORTED :当前以非事务的方式运行, 如果当前有事务就挂起当前事务。
@Transactional(propagation = Propagation.REQUIRED)
public void a(){

}
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void b(){

}
当a方法调用b方法时b方法时没有事务的, 如果b方法出现异常不会回滚,当b方法执行完之后a方法继续执行事务
6、PROPAGATION.NEVER : 当前必须没有事务否则会抛出异常
7、PROPAGATION.NESTED : 嵌套事务, 如果当前没有事务就开启一个事务, 如果当前有事务嵌套在里面嵌套在里面。嵌套事务在异常情况下不会影响外部事务,外部事务一旦出现异常嵌套事务一起回滚。
举例
@Transactional(propagation = Propagation.REQUIRED)
public void a(){

}
@Transactional(propagation = Propagation.NOT_NESTED)
public void b(){

}
public void a(){
//a方法调用b时开启一个内部事务,b方法单独执行这个事务出现异常不影响a事务, a事务如果有异常连同b一起回滚
b();
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值