**三十一、Redis分布式
Redis分布式锁注意事项:
1.设置过期时间:如果获取锁后,系统宕机了,没有释放锁,导致其他线程一致拿不到该锁,影响业务功能,加锁和设置过期时间是原子操作
2.过期时间到了业务操作还没完成:增加守护线程,定时对锁的过期时间进行续期
3.锁的释放:加锁和释放必须是同一个人操作,释放锁先判断是不是自己加的
Redission实现分布式锁,是redis的客户端,基于Lua脚本执行,key的模式过期时间30秒,加锁之后会基于watchDog(看门狗)机制在后台启动一个线程每隔10秒执行一次检查,如果key还在,重置key的生存时间为30秒,redisson也实现可重入锁的机制,再次加锁会将key对应的hash结构中value+1.
Redission的好处:
自动续期功能:watchDog(看门狗)机制在后台启动一个线程每隔10秒执行一次检查,如果key还在,重置key的生存时间为30秒
基本不用写代码只能可以拿来用
过期时间:默认30秒
**三十二、Redis与Memcache区别:
-
数据类型:memcached只支持简单数据类型,键值对
Redis:字符串、集合、列表等
内存管理:redis可以设置过期时间
持久化管理:memcached不提供持久化功能,数据仅保存在内存中;redis支持持久化,将数据保存到磁盘上,防止数据丢失。Reids持久化:RDF、AOF、混合方式
使用场景:
缓存内容只有字符串、不考虑数据持久性、数据过期,可以采用memcached
**三十三、HashMap 的实现原理?
HashMap实际是一种“数组+链表”数据结构,jdk8后数组+链表+红黑树
HashMap是基于哈希表的Map接口的非同步实现。实现HashMap对数据的操作,允许有一个null键,多个null值。
HashMap底层就是一个数组结构,数组中的每一项又是一个链表。数组+链表结构,新建一个HashMap的时候,就会初始化一个数组。Entry就是数组中的元素,每个Entry其实就是一个key-value的键值对,它持有一个指向下一个元素的引用,这就构成了链表,HashMap底层将key-value当成一个整体来处理,这个整体就是一个Entry对象。HashMap底层采用一个Entry【】数组来保存所有的key-value键值对,当需要存储一个Entry对象时,会根据hash算法来决定在其数组中的位置,在根据equals方法决定其在该数组位置上的链表中的存储位置;当需要取出一个Entry对象时,也会根据hash算法找到其在数组中的存储位置, 在根据equals方法从该位置上的链表中取出Entry;
**三十四、HashMap,HashTable,ConcurrentHash的共同点和区别
- HashMap
底层由链表+数组+红黑树实现
可以存储null键和null值
线性不安全
初始容量为16,扩容每次都是2的n次幂
加载因子为0.75,当Map中元素总数超过Entry数组的0.75,触发扩容操作.
并发情况下,HashMap进行put操作会引起死循环,导致CPU利用率接近100%
HashMap是对Map接口的实现
2.HashTable
HashTable的底层也是由链表+数组+红黑树实现。
不能存储null键或null值
它是线性安全的,使用了synchronized关键字。
HashTable实现了Map接口和Dictionary抽象类
Hashtable初始容量为11
3.ConcurrentHashMap
ConcurrentHashMap的底层是数组+链表+红黑树
不能存储null键或null值
ConcurrentHashMap是线程安全的
ConcurrentHashMap使用锁分段技术确保线性安全,JDK8为何又放弃分段锁,是因为多个分段锁浪费内存空间,竞争同一个锁的概率非常小,分段锁反而会造成效率低。
**三十五、面向对象
面向对象的三个基本特征:封装、继承、多态
1)、封装(英语:Encapsulation)是指,一种将抽象性函式接口的实现细节部份包装、隐藏起来的方法。封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问。
2)、继承可以使得子类具有父类的属性和方法或者重新定义、追加属性和方法等,继承可以理解为一个对象从另一个对象获取属性的过程。
3)、多态是同一个行为具有多个不同表现形式或形态的能力。多态性是对象多种表现形式的体现。比如我们说"宠物"这个对象,它就有很多不同的表达或实现,比如有小猫、小狗、蜥蜴等等。那么我到宠物店说"请给我一只宠物",服务员给我小猫、小狗或者蜥蜴都可以,我们就说"宠物"这个对象就具备多态性。
Java 中实现多态的机制是什么?
方法的重写 Overriding 和重载 Overloading 是 Java 多态性的不同表现。重写 Overriding是父类与子类之间多态性的一种表现,重载 Overloading 是一个类中多态性的一种表现
**三十六、抽象类(abstract class)和接口(interface)有什么区别?
1)、抽象类和接口都不能够实例化
2)、接口用interface来修饰
3)、类能实现一个或多个接口,但只能继承一个抽象类
4)、接口里的方法只能是抽象方法、类方法或者默认方法,接口里的方法不能有方法实现,但类方法、默认方法都必须要实现。
**三十七、为什么重载hashCode方法?
一般的地方不需要重载hashCode,只有当类需要放在HashTable、HashMap、HashSet等等hash结构的集合时才会重载hashCode,那么为什么要重载hashCode呢?
如果你重写了equals,比如说是基于对象的内容实现的,而保留hashCode的实现不变,那么很可能某两个对象明明是“相等”,而hashCode却不一样。
这样,当你用其中的一个作为键保存到hashMap、hasoTable或hashSet中,再以“相等的”找另一个作为键值去查找他们的时候,则根本找不到。
**三十八、== 和 equals 的区别是什么?
==是操作符,是比较两个对象的地址或基本类型,equals是比较两个对象的内容,属于Object里的方法。
**三十九、SQL优化
索引查询、避免全表扫描:查询数据库的数据尽量使用索引来查询,避免全表扫描。尽量只查询索引条件的字段
查询数据尽量避免使用or:使用or会导致执行sql的时候进行数据范围的索引扫描或者全表扫描,效率降低。
Where条件中:in和not in、对字段进行表达式操作、对字段进行函数操作都会导致全表搜索
多张表数据查询,使用inner join代替自查询,因为子查询需要在内存中创建临时表来完成这个逻辑上的需要两个步骤的查询工作。
使用like进行数据表查询时,能用%就不建议使用双%:双%查询会导致mysql引擎放弃使用索引而进行全表扫描查询,查询时尽量把%放后面,或者不适用%。
慢查询日志:慢查询日志的使用,在调试的时候开启慢查询,定位的慢查询语句,再做优化策略。关闭/开启语句 Slow_query_log=0|1,Long_query_time=N超过该时间临界点,就为慢查询
mysql查看sql的执行计划,以此来分析sql执行缓慢的问题所在
**四十、锁
公平锁:指多个线程按照申请锁的顺序来获取锁。
非公平锁:指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。有可能,会造成优先级反转或者饥饿现象。
可重入锁:指的是可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁(前提得是同一个对象或者class),例如ReentrantLock和synchronized都是可重入锁。防止死锁
不可重入锁:不可递归调用,递归调用就发生死锁
独享锁:是指该锁一次只能被一个线程所持有。 共享锁:是指该锁可被多个线程所持有。
悲观锁:认为对于同一个数据的并发操作,一定是会发生修改的,哪怕没有修改,也会认为修改。因此对于同一个数据的并发操作,悲观锁采取加锁的形式。悲观的认为,不加锁的并发操作一定会出问题
乐观锁:则认为对于同一个数据的并发操作,是不会发生修改的。在更新数据的时候,会采用尝试更新,不断重新的方式更新数据。乐观的认为,不加锁的并发操作是没有事情的。
Java锁机制可归为Sychornized锁和Lock锁两类。
Synchronized是基于JVM来保证数据同步的,底层使用指令码方式来控制锁的,映射成字节码指令就是增加来两个指令:monitorenter和monitorexit。当线程执行遇到monitorenter指令时会尝试获取内置锁,如果获取锁则锁计数器+1,如果没有获取锁则阻塞;当遇到monitorexit指令时锁计数器-1,如果计数器为0则释放锁。
Lock底层是CAS乐观锁,是在硬件层面,通过特殊的CPU指令实现数据同步的,依赖AbstractQueuedSynchronizer类,把所有的请求线程构成一个CLH队列。而对该队列的操作均通过Lock-Free(CAS)操作。与synchronized不同的是,Lock锁是纯Java实现的,与底层的JVM无关。在java.util.concurrent.locks包中有很多Lock的实现类,常用的有ReentrantLock、ReentrantReadWriteLock,java的Lock接口的子类就是借助AQS来实现了lock和unlock。
ReentrantLock原理
ReentrantLock主要利用CAS+AQS队列来实现,支持公平锁和非公平锁。ReentrantLock的基本实现可以概括为:先通过CAS尝试获取锁。如果此时已经有线程占据了锁,那就加入AQS队列并且被挂起。当锁被释放之后,排在CLH队列队首的线程会被唤醒,然后CAS再次尝试获取锁。此时,如果:
非公平锁:如果同时还有另一个线程进来尝试获取,那么有可能会让这个线程抢先获取
公平锁:如果同时还有另一个线程进来尝试获取,当它发现自己不是在队首的话,就会排到队尾,由队首的线程获取到锁
ReentrantLock默认构造器初始化为NonfairSync对象,由lock()和unlock的源码可以看到,它们分别调用了sync对象的lock()和release(1)方法。
Lock与synchronized 的区别
synchronized会自动释放锁、ReentrantLock需要手动释放锁(在finally块中显示释放锁)
Synchronized是非公平锁,ReentrantLock是可以是公平也可以是非公平的(默认非公平)
synchronized是在JVM层面上实现的,Lock是用CAS来实现的