这篇主要讲解的是Mybatis和多线程
Mybatis
Mybatis的一级、二级缓存?
一级缓存存储的作用域是session,当session flush或者close之后,session中的缓存会失效,此时一级缓存就开启
二级缓存和一级缓存机制相同,hashmap存储,作用域为mapper,二级缓存一般不用,因为他不好控制缓存的刷新,我们一般是用的是redis
MyBatis 的接口mapper可以重载么?
不可以,mybatis查找mapper内的方法是靠方法名,和参数无关。所以,对于mapper接口,Mybatis禁止方法重载
Hibernate 和 MyBatis 的区别?
mybatis是一个小巧方便,半自动化持久层框架,学习起来比较容易
hibernate是一个全自动化持久层框架,做sql优化难,学习也比较困难
#{}和${}的区别是什么?
- #{}是预编译处理,效率要高点,${}是字符串替换。
- #{} 可以有效的防止SQL注入,提高系统安全性。${}有sql注入的风险
多线程
实现线程的方法有哪些?
1、继承thread类
2、实现runable接口
3、实现callable接口(可以获取线程的返回结果)
runable和callable的区别?
1、runable是实现run(),callable是实现call()
2、runable无返回值,callable有返回值
3、runable不抛异常,callable抛异常
如何让线程的有序执行?
采用join
countdownlatch类,首先有countdown()和await(),当调用countdown()时,计数器不为0的时候就会执行await(),如果技术器为0则会执行下一个任务
采用 Executors的单线程池创建方式 Executors.newSingleThreadExecutor();
如何获取线程的返回结果?
实现callable接口,返回Futrue,通过get方法可以获取结果
线程数过多会造成什么情况?
1、消耗资源
2、占用cpu
3、降低了稳定性
解决线程安全的方式有哪些?
1、加synchronized锁
2、加lock锁 此时采用的是Reentratlock锁
3、用ThreadLocal
4、atomic
5、提供了一些线程安全的类比如ConcurrentHashmap
线程池的底层原理
创建线程池的时候,开始一个线程也没有,随着任务的提交创建线程,当前线程如果小于corePoolSize核心线程数,创建线程, 否则(当前线程大于或等于核心线程数)放入LinkedBlockingQueue队列,如果队列没有满,继续放入,如果队列满了, 判断是否小于最大线程数,如果小于继续创建线程,否则拒绝策略(默认拒绝策略抛异常)
拒绝策略有哪些?
1、使用线程解决
2、直接拒绝不抛异常
3、直接拒绝抛出异常(默认)
4、将最早的线程舍弃,将最新线程添加
创建线程池的方法有哪些?
1、单一线程池(始终只有一个线程)
2、定时线程池(在固定时间段内使用线程)
3、定长线程池(自定义设置线程大小 一般使用)
4、可缓存线程池(无限制的线程大小)
synchroized的底层原理
synchroized是通过监视器monitor来完成的,如果monitor被占用时会处于死锁的状态,线程需要 执行monitorenter指令去尝试获取monitor的所有权,如果monitor的进入数为0,那么进入现场进入monitor,然后将进入数设置为1,此线程为monitor的所有者,如果线程已经有monitor需要重新进入,monitor为+1,如果已经占用了monitor,则该线程进入等待的状态,直到monitor的进入数为0时,再去重新获取所有权。
ReentratLock的底层原理
ReentratLock主要是通过AQS+CAS来实现的
先通过CAS去获取锁,如果锁获取到了就将线程放入AQS队列中去,如果锁释放了,就会释放AQS队列中的首个线程,在通过CAS去尝试获取锁。
底层是基于AQS实现的,每个都有自己的内部类
lock的存储结构:一个int类型(用来存放锁的状态变更) 一个是双向链表(用于存储等待中的线程)
lock获取锁的过程:本质上是通过CAS来修改状态,如果获取到锁之后就更改锁的状态,没有获取到锁就放到等待链表中进行等待。
lock释放锁的过程:修改状态,调整链表
ThreadLocal原理?
当多个线程操作同一变量且互不干扰的情况下,可以使用threadlocal来解决,它会每个线程都创建一个副本,线程内部都会创建一个变量。在底层有一个巨大的map来存放线程,如果在使用完线程之后没有释放,那么会造成一个内存溢出的情况
ThreadLocal为什么会造成内存溢出?
因为底层是巨大的map,而map的key是弱引用,value是强引用,而进行回收的时候可以回收掉key,value是回收不掉的,解决方法可以直接使用自带的remove
volatile 有什么作用?
- 防止指令重排序,2.保证各个线程之间的可见性
AQS底层原理
AQS维护volatile下的共享资源state,如果获取到锁了那么就去改变state的状态,如果没有获取到那么就进入一个阻塞的状态,放入阻塞队列中去
原理就是:AQS实际是通过CLH队列操作的,CLH队列是一个虚拟的双向队列,通过CAS去获取,获取到修改state状态,获取不到就放入阻塞队列中去
CAS原理
CAS(compare and swap)是比较交换,是一种乐观锁, VAB V是内存预期要修改的变量,A是要修改的预期值,B是新值,如果用户要去改变内存中的值,那么A要去和V去做对比,如果相等了那么B的值就是A的值。
会出现一个ABA的问题,如果有一个人吧值从原来的1改成了2,后又吧2改成了1,那么别人认为这个操作是无用的,其实对于CAS来说是用的,就会出现ABA的问题,解决方式 每修改一次就给版本号上+标识
synchronized 和 lock 有什么区别?
synchronized 是java语言的关键字,lock是一个类,通过这个类可以实现同步访问
synchronized我们一般是锁代码块,lock一般是我们需要创建一把锁,然后执行任务。最后需要手动释放锁
synchronized的底层是基于JVM指令完成,它会在代码块中加入monitorenter和monitorexit执行,而lock是通过代码实现的,底层基于AQS实现的
synchronized 获得锁和释放锁的方式都在块结构中,是 自动释放锁,lock需要手动释放,并且必须在finally中,否则会造成死锁