1.java多线程
1.1 java多线程通信?
多线程并发编程时,难免会遇到线程间的通信问题。线程通信方式的思想大体上来说可以分为两种:共享和传递。
共享的实现方式可以是共享变量、共享文件、数据库、网络等。传递的实现方式可以是消息队列、生产者-消费者模型等。
(1)共享变量:
使用volatile 关键字定义共享变量 private static volatile Boolean odd = true。
多个线程同时监听一个变量,当这个变量发生变化的时候 ,线程能够感知并执行相应的业务。
volatile 保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新。
(2)使用Object类的wait() 和 notify() 方法:
wait和 notify必须配合synchronized使用,wait方法释放锁,notify方法不释放锁。
(3)基本LockSupport实现线程间的阻塞和唤醒:
LockSupport.park(); 阻塞
LockSupport.unpark(threadB); 唤醒
1.2 java多线程实现方式?
(1)继承Thread类创建线程。
(2)实现Runnable接口创建线程。 多,多实现单继承。
(3)java的Executors线程池。减少对象创建, 提供定时执行、定期执行、单线程、并发数控制等功能。
1.3 java什么是线程池?
创建线程需要花费资源和时间,如果等任务来创建线程那么响应时间就变长。
在程序启动时创建线程来响应处理就是线程池。
1.4 java多线程start()与run()方法区别?
start():Thread类start()来启动一个线程,这时此线程是处于就绪状态, 并没有运行。 然后通过此Thread类调用方法run()来完成其运行。
run()称为线程体,它包含了要执行的这个线程的内容。
run(): run()方法当作普通方法的方式调用,程序还是要顺序执行。没有达到多线程目的。
1.5 java多线程中wait()与sleep()区别?
(1)wait是Object方法,sleep是线程方法。
(2)wait会释放锁,sleep不释放锁。
(3)wait需要依赖synchronized关键字,sleep方法不依赖于同步器synchronized。
1.6 synchronized与static synchronized区别?
synchronized锁的是对象
static synchronized锁的是类
2.java基础
2.1 java多态?
不同类的对象对同一消息作出不同的响应就叫做多态。
多态存在的三个条件:
(1)有继承关系。
(2)子类重写父类方法。
(3)父类引用指向子类对象,父类调用方法会调用子类重写后方法。FU fu=new ZI();
2.2 java中final关键字?
(1)final成员变量表示常量,只能被赋值一次,赋值后值不再改变。
(2)final方法不能被子类的方法重写,但可以被继承。
(3)final类不能被继承,没有子类,final类中的方法默认是final的。
(4)final不能用于修饰构造方法。
2.3 java中static关键字?
(1)静态变量。类加载只为分配一次内存,在加载类的过程中完成静态变量的内存分配,可用类名直接访问。
(2)静态方法。静态方法可以直接通过类名调用。静态方法不能访问非静态方法。
(3)static代码块。类加载的时候就会依次执行一次静态代码块。
2.4 接口保证幂等性是基本的要求,那么幂等性你们是怎么做的?
接口的幂等性实际上就是接口可重复调用,在调用方多次调用的情况下,接口最终得到的结果是一致的。有些接口可以天然的实现幂等性,比如查询接口,对于查询来说,你查询一次和两次,对于系统来说,没有任何影响,查出的结果也是一样。
除了查询功能具有天然的幂等性之外,增加、更新、删除都要保证幂等性。那么如何来保证幂等性呢?
2.4.1 全局唯一ID
如果使用全局唯一ID,就是根据业务的操作和内容生成一个全局ID,在执行操作前先根据这个全局唯一ID是否存在,来判断这个操作是否已经执行。如果不存在则把全局ID,存储到存储系统中,比如数据库、redis等。如果存在则表示该方法已经执行。
从工程的角度来说,使用全局ID做幂等可以作为一个业务的基础的微服务存在,在很多的微服务中都会用到这样的服务,在每个微服务中都完成这样的功能,会存在工作量重复。另外打造一个高可靠的幂等服务还需要考虑很多问题,比如一台机器虽然把全局ID先写入了存储,但是在写入之后挂了,这就需要引入全局ID的超时机制。
使用全局唯一ID是一个通用方案,可以支持插入、更新、删除业务操作。但是这个方案看起来很美但是实现起来比较麻烦,下面的方案适用于特定的场景,但是实现起来比较简单。
2.4.2 防重复提交策略
上述的保证幂等方案是分成两步的,第②步依赖第①步的查询结果,无法保证原子性的。在高并发下就会出现下面的情况:第二次请求在第一次请求第②步订单状态还没有修改为‘已支付状态’的情况下到来。
既然得出了这个结论,余下的问题也就变得简单:把查询和变更状态操作加锁,将并行操作改为串行操作。
2.4.3 防重表
这种方法适用于在业务中有唯一标的插入场景中,比如在以上的支付场景中,如果一个订单只会支付一次,所以订单ID可以作为唯一标识。这时,我们就可以建一张去重表,并且把唯一标识作为唯一索引,
在我们实现时,把创建支付单据和写入去去重表,放在一个事务中,如果重复创建,数据库会抛出唯一约束异常,操作就会回滚。
2.4.4多版本控制
这种方法适合在更新的场景中,比如我们要更新商品的名字,这时我们就可以在更新的接口中增加一个版本号,来做幂等 boolean updateGoodsName(int id,String newName,int version);
2.4.5状态机控制
这种方法适合在有状态机流转的情况下,比如就会订单的创建和付款,订单的付款肯定是在之前,这时我们可以通过在设计状态字段时,使用int类型,并且通过值类型的大小来做幂等,比如订单的创建为0,付款成功为100。付款失败为99 在做状态机更新时,我们就这可以这样控制 update `order` set status=#{status} where id=#{id} and status<#{status}
2.5 @Transactional来控制事务,那么能不能说出一些事务不生效的场景?
2.5.1数据库引擎不支持事务
2.5.2没有被spring管理
我们会把事务注解加到service层,如果没有@Service注解,这个类就不会被加载成一个Bean,那这个类就不会被spring管理,事务自然就失效了。。
2.5.3 就是在@Transactional方法内部捕获了异常,没有在catch代码块里面重新抛出异常,事务也不会回滚。
所以在阿里巴巴的Java开发者手册里面有明确规定,在 @Transactional的方法里面捕获了异常,必须要手动回滚, 代码如下: @Override @Transactional public void insertOne() { try { UserEntity userEntity = new UserEntity(); userEntity.setUsername("Michael_C_2019"); //插入到数据库 userMapper.insertSelective(userEntity); //手动抛出异常 throw new IndexOutOfBoundsException(); } catch (IndexOutOfBoundsException e) { e.printStackTrace(); TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); }
2.5.4 同一个类中方法调用,导致@Transactional失效
开发中避免不了会对同一个类里面的方法调用,比如有一个类Test,它的一个方法A,A再调用本类的方法B(不论方法B是用public还是private修饰),但方法A没有声明注解事务,而B方法有。则外部调用方法A之后,方法B的事务是不会起作用的。这也是经常犯错误的一个地方。 那为啥会出现这种情况?其实还是由于使用Spring AOP代理造成的,因为只有当事务方法被当前类以外的代码调用时,才会由Spring生成的代理对象来管理。 //@Transactional @GetMapping("/test") private Integer A() throws Exception { CityInfoDict cityInfoDict = new CityInfoDict(); cityInfoDict.setCityName("2"); /** * B 插入字段为 3的数据 */ this.insertB(); /** * A 插入字段为 2的数据 */ int insert = cityInfoDictMapper.insert(cityInfoDict); return insert; } @Transactional() public Integer insertB() throws Exception { CityInfoDict cityInfoDict = new CityInfoDict(); cityInfoDict.setCityName("3"); cityInfoDict.setParentCityId(3); return cityInfoDictMapper.insert(cityInfoDict); }
2.5.5 @Transactional修饰的方法为非public方法
根据spring官网,@Transactional只能用于public的方法上,否则事务不会生效,如果非要用在非public方法上,可以开启AspectJ代理模式。AspectJ使用加载时织入的方式,支持所有的pointcut,因此可以支持内部方法的事务设置。
2.6 String, StringBulider, StringBuffer区别?
String适用于少量字符串操作。
StringBuilder单线程大量操作,不是线程安全的。
StringBuffer多线程大量操作,线程安全。
String常量,StringBuilder和StringBuffer字符串变量。
运行速度StringBuilder>StringBuffer>String。
2.7 SpringMVC和Struct2区别?
SpringMVC入口是servlet, Struct2入口是filter
SpringMVC是基于方法, Struct2基于类
2.8 字符流和字节流的区别?
字符流用于处理文本,
字节流处理任何数据类型,计算器中数据以字节存储
2.9 Exception和error区别?
exception需要程序捕捉或者处理掉的异常
error系统级的错误
2.10 JAVA构造函数?
方法名与类名相同,没有返回值,
作用: 对象一建立就自动调用构造函数,
2.11 cookie和session的区别?
cookie数据存储在浏览器上,session在服务器上
cookie不安全,session安全
2.12 get与post请求区别?
get: 请求参数会显示在浏览器地址上,不安全, 长度有限制请求参数1k,get没有请求体
post: 请求参数不会显示在浏览器地址,相对安全,参数无限制
2.13 JAVA动态代理?
给方法添加预处理或者添加后续操作,不干预正常业务,SpringAop原理就是基于动态代理实现的.
实现方式:
1.JDK 实现invacationHandler接口 invoke()方法
2.CGLIB 实现methodInterceptor接口 intercept()方法
区别: JDK反射机制提供的代理.
CGLIB是利用asm开源包修改代理对象的字节码来处理.
3.数据库
3.1 mysql explain 查看sql语句执行计划
3.2 mysql查看进程 show processList
3.3 mysql触发器
一张表发生某件事件(插入,删除,更新), 就会自动触发预先编写好的sql语句。create trigger
增加程序复杂度使后期维护变得困难,尽量在代码中处理。
3.4 数据库的隔离级别
3.4.1读取未提交的。
读未提交,顾名思义,就是可以读到未提交的内容。
因此,在这种隔离级别下,查询是不会加锁的,也由于查询的不加锁,所以这种隔离级别的一致性是最差的,可能会产生“脏读”、“不可重复读”、“幻读”。
如无特殊情况,基本是不会使用这种隔离级别的
3.4.2.读取已提交的。
读提交,顾名思义,就是只能读到已经提交了的内容。
这是各种系统中最常用的一种隔离级别,也是SQL Server和Oracle的默认隔离级别。
3.可重复读。
同一事务下,两次相同查询结果可能不一样,而它也是MySql的默认隔离级别。
4.串行化。
这是数据库最高的隔离级别,这种级别下,事务“串行化顺序执行”,也就是一个一个排队执行。
这种级别下,“脏读”、“不可重复读”、“幻读”都可以被避免,但是执行效率奇差,性能开销也最大,所以基本没人会用。
4.注解
5.集合
5.1 hashcode作用?
hashcode主要用于查找的快捷性,在散列存储结构中确定对象的存储地址.
5.2 ArrayList原理?
ArrayList底层数据结构是一个数组,数组元素类型为Object类型,对其操作底层都是基于数组的,
线程不安全,可使用synchronied关键字保证代码安全.
5.3 HashMap原理?
map以key-value键值对进行数据存储. 当传入数据时先将key取出, 利用hash函数转换成hash值, 再用散列算法得到存入下标index.
如果下标有数据用equal比较hash值是否相同, 再比较key值是否相同, 相同覆盖不同则存储.
hashmap默认长度16, 达到上限自动扩容
5.4 collection与collections区别?
collection 是java.util下接口,集合结构父类
collections是java.util下类,操作集合的静态方法
5.5 ArrayList与LinkedList区别?
ArrayList基于数组结构,LinkedList基于链表结构
随机访问 ArrayList优于LinkedList
新增删除 LinkedList优于ArrayList
5.6 ArrayList与Vector区别?
vector是线程安全的, Array不是, Vector效率比Array低.
5.7 HashSet与HashMap区别?
Set: 实现set接口, 存储对象, add加元素
Map:实现map接口, 存储键值对 ,put加元素, 更快
5.8 HashMap和HashTable区别?
都实现map接口, hashmap线程不安全但比hashtable快, 单线程用HashMap多线程用HashTable
5.9 数组和链表的区别?
数组::将元素在内存中连续存放,每个元素占用内存相同,可以通过下标迅速访问数组任何元素。增加,刪除就会移动大量元素在内存中腾出空间,适用快速访问。
链表: 链表中的元素在内存中不是顺序存储的,通过元素中指针联系在一起的,适用于增删。