hashmap的底层实现
使用的是数组+链表+红黑树
详情戳这里
LinkedList和ArrayList的区别
LinkedList:基于链表实现,增删O(1)查询O(n)
ArrayList:基于数组实现,增删O(n),查询O(1)
线程与进程的区别
一个进程就是一个应用程序,一个进程中可以有多个线程
事务ACID
原子性:一个事务是一个不可分割的单位,其中的操作要么都做,要么都不做
一致性:事务执行结束后,数据库的完整性约束没有被破坏,事务执行前后都是合法的数据
隔离性:事务内部的操作与其他事务是隔离的,并发执行的各个事务之间不能互相干扰
持久性:事务一旦提交,他对数据库的改变就应该是永久性的,接下来的其他操作或故障不应该对其有任何影响
如何预防死锁
给加锁设置时间,尝试获取锁的时候一旦超过时间就自动放弃对该锁的请求,并释放自己占用的锁
事务的隔离级别
1.读未提交:事务A可以读取到事务B修改过但未提交的数据。—脏读,不可重复读,幻读
2.读已提交:事务B只能在事务A修改过并且已提交后才能读取到事务B修改的数据,—解决脏读,但是仍然有幻读和不可重复读
3.可重复度:事务B只能在事务A修改过数据并提交后,自己也提交事务后,才能读取到事务A修改的数据。—解决脏读,不可重复读,但是仍然有幻读
4.可串行化:一旦事务中有写操作,那么只能等前面的事务全部执行完成后才能执行—解决了全部的问题
Java基础数据类型
byte char short int long double boolean float
面向对象特征
封装,继承,多态
接口与抽象类区别
- Java抽象类可以提供某些方法的部分实现,而Java接口不可以(java 1.8中可以定义default方法体)
- 接口强调特定功能的实现,而抽象类强调所属关系。
- 接口成员变量默认为
public static final
,必须赋初值,不能被修改,其所有的成员方法都是public
、abstract
的,抽象类中成员变量默认default
,可在子类中被重新定义,也可被重新赋值;抽象方法被abstract
修饰,不能被private
、static
、synchronized
和native
等修饰,必须以分号结尾,不带花括号。 - 接口被用于常用的功能,便于日后维护和添加删除,而抽象类更倾向于充当公共类的角色,不适用于日后重新对立面的代码修改。功能需要累积时用抽象类,不需要累积时用接口。
重写与重载区别
重写发生在父子类之间,子类重写父类的方法,子类重写的方法权限不能低于父类的权限,重载发生在同一个类中,方法名相同,返回值相同,但是参数不同
线程安全不线程不安全
两个线程同时修改同一个变量就会产生安全问题,可能会导致同一线程两次读取的值不相同
地址栏输入url回车后发生了什么
1.浏览器查找域名的IP地址
2.浏览器与目标服务器建立TCP连接
3.浏览器给web服务器发送一个HTTP请求
4.服务器处理请求
5.服务器发出一个HTML响应
6.释放TCP连接
7.浏览器显示页面
TCP协议和UDP协议的区别是什么
- TCP协议是有连接的,有连接的意思是开始传输实际数据之前TCP的客户端和服务器端必须通过三次握手建立连接,会话结束之后也要四次挥手结束连接。而UDP是无连接的
- TCP协议保证数据按序发送,按序到达,提供超时重传来保证可靠性,但是UDP不保证按序到达,甚至不保证到达,只是努力交付,即便是按序发送的序列,也不保证按序送到。
- TCP协议所需资源多,TCP首部需20个字节(不算可选项),UDP首部字段只需8个字节。
- TCP有流量控制和拥塞控制,UDP没有,网络拥堵不会影响发送端的发送速率
- TCP是一对一的连接,而UDP则可以支持一对一,多对多,一对多的通信。
- TCP面向的是字节流的服务,UDP面向的是报文的服务。
三次握手建立连接时,发送方再次发送确认的必要性?
主要是为了防止已失效的连接请求报文段突然又传到了B,因而产生错误。假定出现一种异常情况,即A发出的第一个连接请求报文段并没有丢失,而是在某些网络结 点长时间滞留了,一直延迟到连接释放以后的某个时间才到达B,本来这是一个早已失效的报文段。但B收到此失效的连接请求报文段后,就误认为是A又发出一次 新的连接请求,于是就向A发出确认报文段,同意建立连接。假定不采用三次握手,那么只要B发出确认,新的连接就建立了,这样一直等待A发来数据,B的许多 资源就这样白白浪费了。
四次挥手释放连接时,等待2MSL的意义?
- 为了保证A发送的最后一个ACK报文段能够到达B。这个ACK报文段有可能丢失,因而使处在LAST-ACK状态的B收不到对已发送的FIN和ACK 报文段的确认。B会超时重传这个FIN和ACK报文段,而A就能在2MSL时间内收到这个重传的ACK+FIN报文段。接着A重传一次确认。
- 防止上面提到的已失效的连接请求报文段出现在本连接中,A在发送完最有一个ACK报文段后,再经过2MSL,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失。
常见的应用中有哪些是应用TCP协议的,哪些又是应用UDP协议的,为什么它们被如此设计?
- 多播的信息一定要用udp实现,因为tcp只支持一对一通信。
- 如果一个应用场景中大多是简短的信息,适合用udp实现,因为udp是基于报文段的,它直接对上层应用的数据封装成报文段,然后丢在网络中,如果信息量太大,会在链路层中被分片,影响传输效率。
- 如果一个应用场景重性能甚于重完整性和安全性,那么适合于udp,比如多媒体应用,缺一两帧不影响用户体验,但是需要流媒体到达的速度快,因此比较适合用udp
- 如果要求快速响应,那么udp听起来比较合适
- 如果又要利用udp的快速响应优点,又想可靠传输,那么只能考上层应用自己制定规则了。
- 常见的使用udp的例子:ICQ,QQ的聊天模块。
静态内部类会被编译成几个class?
1个class,名称为外部类名$内部类名.class
为什么内部类可以访问外部类的private的方法?
使用private
关键字修饰了某个成员,只有这个成员所在的类和这个类的方法可以使用,其他的类都无法访问到这个private成员。
CAS是什么?
CAS:compareAndSet比较工作内存的值和主内存的值,如果这个值是期望的,那么就执行操作,如果不是就一直循环(底层是自旋锁)
CAS底层是靠Unsafe类实现的,unsafe类是java的后门,可以使用这个类操作内存
底层其实就是利用内存偏移量来计算下标,然后判断当前值是否满足期望,满足就修改
缺点:
- 循环会耗时
- 一次性只能保证一个共享变量的原子性
- 存在ABA问题
ABA问题是什么?
ABA问题
- 在多线程场景下CAS会出现ABA问题,关于ABA问题这里简单科普下,例如有2个线程同时对同一个值(初始值为A)进行CAS操作,这三个线程如下
- 1.线程1,期望值为A,欲更新的值为B
- 2.线程2,期望值为A,欲更新的值为B
- 线程1抢先获得CPU时间片,而线程2因为其他原因阻塞了,线程1取值与期望的A值比较,发现相等然后将值更新为B,然后这个时候出现了线程3,期望值为B,欲更新的值为A,线程3取值与期望的值B比较,发现相等则将值更新为A,此时线程2从阻塞中恢复,并且获得了CPU时间片,这时候线程2取值与期望的值A比较,发现相等则将值更新为B,虽然线程2也完成了操作,但是线程2并不知道值已经经过了A->B->A的变化过程。
ABA问题带来的危害:
- 小明在提款机,提取了50元,因为提款机问题,有两个线程,同时把余额从100变为50
线程1(提款机):获取当前值100,期望更新为50,
线程2(提款机):获取当前值100,期望更新为50,
线程1成功执行,线程2某种原因block了,这时,某人给小明汇款50
线程3(默认):获取当前值50,期望更新为100,
这时候线程3成功执行,余额变为100,
线程2从Block中恢复,获取到的也是100,compare之后,继续更新余额为50!!!
此时可以看到,实际余额应该为100(100-50+50),但是实际上变为了50(100-50+50-50)这就是ABA问题带来的成功提交。 - 解决方法: 在变量前面加上版本号,每次变量更新的时候变量的版本号都+1,即A->B->A就变成了1A->2B->3A。
什么是AQS?
AQS:AbstractQuenedSynchronizer抽象的队列式同步器。是除了java自带的synchronized关键字之外的锁机制。
AQS的全称为(AbstractQueuedSynchronizer),这个类在java.util.concurrent.locks包
AQS的核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并将共享资源设置为锁定状态,如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。
CLH(Craig,Landin,and Hagersten)队列是一个虚拟的双向队列,虚拟的双向队列即不存在队列实例,仅存在节点之间的关联关系。AQS是将每一条请求共享资源的线程封装成一个CLH锁队列的一个结点(Node),来实现锁的分配。
用大白话来说,AQS就是基于CLH队列,用volatile修饰共享变量state,线程通过CAS去改变状态符,成功则获取锁成功,失败则进入等待队列,等待被唤醒。
AQS是自旋锁:在等待唤醒的时候,经常会使用自旋(while(!cas()))的方式,不停地尝试获取锁,直到被其他线程获取成功
泛型 List<Integer>
与 List<String>
这两个的getClass是否相同?
相同,java有泛型擦除机制,List<String>
和 List<Integer>
在编译后都变成 List
。
说说MySQL Innodb 索引底层实现?
底层使用B-树,B+树对索引进行管理
mysql的join关键字?
分为内连接inner join
和外连接,外连接分为左连接left join
,右连接right join
两表连接查询的时候,如果不加约束就会产生笛卡尔积效应
mysql的GroupBy关键字?
GROUP BY 语句根据一个或多个列对结果集进行分组。一般会将分组函数和GROUP BY 联合使用
volatile关键字的作用?
- 保证可见性 在一处修改主内存的值,另一个线程可以立即知道
- 不保证原子性 原子性就是不可分割的意思 线程A在执行任务的时候,不能被打扰的,也不能被分割,要么同时成功,要么同时失败
- 禁止指令重排 指令重排:自己写的程序,计算机并不是按照自己写的那样去执行的
说说指令重排?
解释型语言是在运行期间执行编译+运行动作,所以运行效率较编译型语言低。
Java中有两个编译期:1、调用javac命令将Java代码编译成Java字节码;2、Unix派系平台上调用gcc命令将openjdk源码编译成汇编代码。
编译期间,Java中所谓的指令重排主要是说编译openjdk时的指令重排,将Java代码编译成Java字节码是没有做指令重排的。即你加不加volatile,生成的字节码文件是一样的。
指令重排是编译器优化中的一种,编译openjdk是启用了O2级编译器优化
O2级优化做了哪些优化?比如优化无效代码、编译期完成简单运算、处理编译期屏障……
因为现在的CPU都是采用乱序执行,这样在运行程序的过程中就带来了指令重排的现象。
HashMap多线程下resize()死循环?
多线程下,如果一个线程进行了resize()并进行了rehash(),加入线程1进行到第一步就被挂起,线程2开始resize(),线程2resize()完成之后,线程1又开始继续进行resize()此时就有链表成环的可能
hashtable和hashmap区别
- hashmap线程不安全,hashtable线程安全
- HashMap继承自AbstractMap类,Hashtable继承自Dictionary类 但是都实现了Map接口
- HashMap是没有contains方法的,而包括containsValue和containsKey方法,hashtable则保留了contains方法,效果同containsValue,还包括containsValue和containsKey方法。
- Hashmap是允许key和value为null值的,用containsValue和containsKey方法判断是否包含对应键值对;HashTable键值对都不能为空,否则包空指针异常。
- 两个计算hash值的方法不同,hashMap计算方法,hashtable直接使用hashCode来计算
- HashMap在求hash值对应的位置索引时,
index = (n - 1) & hash
。将哈希表的大小固定为了2的幂,因为是取模得到索引值,HashTable在求hash值位置索引时计算index的方法int index = (hash & 0x7FFFFFFF) % tab.length;
- HashMap 哈希扩容必须要求为原容量的2倍,而且一定是2的幂次倍扩容结果,而且每次扩容时,原来数组中的元素依次重新计算存放位置,并重新插入;而Hashtable扩容为原容量2倍加1;
- hashmap在出现hash冲突且冲突数量大于8的时候就会进化为红黑树,hashtable只有链表