Java面试题(上)
1.Java中线程的状态有哪些?
Java中线程的状态总共有六种。新建(NEW)、可运行状态(Runnable)、终止状态(TERMINATED)、阻塞状态(Blocked)、等待状态(wait)、有限时间等待状态(timedwait)
新建状态:当创建一个线程的时候处于新建状态
可运行状态:当调用start方法开启线程的时候处于就绪状态
终止状态:表示该线程已经执行完毕。
阻塞状态:当多个线程在执行任务时,其中一个线程先拿到锁执行任务,其他线程要等拿到锁的线程执行完之后再拿到锁处理任务,等待锁的过程就处于阻塞状态
等待状态:没有时间的等待,获取锁的线程,调用锁对象的调用wait方法,其他线程调用锁对象.notify()方法或notifyAll()方法唤醒线程
有时限等待:调用wait(long)方法或sleep(long)方法设置线程休眠时间,时间结束后线程会自动醒来
2.wait和sleep方法有什么区别
方法归属不同:wait方法属于Object类的实例方法,sleep属于Thread类的静态方法
醒来时机不同:调用wait(long)和sleep(long)的线程会在等待相应毫秒后醒来
wait()和wait(long)都可以被notify()唤醒,wait()如果不唤醒就一直等下去
锁特性不同:wait方法基于锁对象调用,可以在任何地方使用,方法执行后会释放对象锁,允许其他线程获得 该对象锁
而sleep方法只能在同步代码块中执行,并不会释放对象锁
3.如何停止一个线程?
调用Thread类的stop方法(过时方法)
使用退出标志终止线程:Java 关键字 volatile定义一个退出标志,设定默认值为false,设置循环条件为false,在主线程中改变退出标志的值为true则可以终止线程
使用interrupt方法终止线程:在线程类中定义一个成员变量isInterrupted,设置默认值为false,提供set方法,在主线程中通过set方法改变默认值则可以终止线程
4.Java中创建线程池有哪些方式?
- 通过
ThreadPoolExecutor
创建的线程池
ThreadPoolExecutor 最多可以设置 7 个参数:
corePoolSize:核心线程数,线程池中始终存活的线程数
maximumPoolize:最大线程数,线程池中允许的最大线程数,当线程池的任务队列满了之后可以创建的最大线程数
keepAliveTime:最大线程数可以存活的时间,当线程中没有任务执行时,最大线程就会销毁一部分,最终保持核心线程数量的线程。
unit:单位是和参数 3 存活时间配合使用的,合在一起用于设定线程的存活时间
workQueue:一个阻塞队列,用来存储线程池等待执行的任务,均为线程安全threadFactory:线程工厂,主要用来创建线程
handler:拒绝策略,拒绝处理任务时的策略,默认策略为 AbortPolicy
- 通过
Executors
创建的线程池
使用Executors 工具类创建线程池对象弊端如下:
1) FixedThreadPool 和 SingleThreadPool:允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM(内存溢出)
2)CachedThreadPool:允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM
5.InnoDB存储引擎的特点:
- 支持事务,提供ACID事务支持
- 行级锁定,可以实现高并发的读写操作,减少数据锁定的冲突
- 外键约束,支持外键关联,保证数据的完整性和一致性
6.并发和并行
并发
进程中的线程由CPU负责调度执行,但是CPU同时处理线程的数量是有限的,为了保证全部线程都能执行到,CPU采用轮询机制为系统的每个线程服务。由于CPU切换的速度很快,给人感觉这些线程在同时执行,这就是并发(并发就是多条线程交替执行)
并行
多核情况下,多个线程同时被CPU调度执行
7.HashMap底层数据结构
底层数据结构是哈希表结构
JDK8之前的哈希表:数组+链表
JDK8之后的哈希表:数组+链表+红黑树
数组的同一个索引位置有多个元素,并且元素超过了8个,数组长度>=64则以红黑数形式存储。
数组的同一索引位置有多个元素,并且元素在8个以内则以链表的形式存储。
JDK7:链表采用头插法,新元素往链表的头部添加
JDK8:链表采用尾插法,新元素往链表的尾部添加
8.HashMap是线程安全的吗?如何得到线程安全的Map
HashMap是线程不安全的,JDK7扩容会导致死循环和数据丢失,JDK8由于哈希碰撞会出现数据覆盖的情况
Map实现线程安全的三种方案:
使用Hashtable(不推荐使用)
在增删改方法上使用了synchronized机制,同一时刻只能有一个线程在执行,因为对整个表进行锁定,当线程越来越多时,对map的竞争越激烈,效率降低
使用Collections.synchronizedMap(new Hashtable())(不推荐使用)
使用工具类里面的静态方法,利用装饰者设计模式将传进来的Hashtable包装成同步的,也就是在增删改方法上添加锁机制
使用ConcurrentHashMap(推荐使用)
对整个表进行加锁,而ConcurrentHashMap是把表进行分段,初始情况下分成16段,每一段都有一把 锁,当多个线程访问不同的段时,因为获取到的锁是不同的,所以可以并发的访问,效率高
9.单例模式
懒汉式**(存在线程安全问题)**
懒汉式单例设计模式是真正用到单例对象的时候才会去创建这个单例对象
**线程安全问题:**由于懒汉式是当静态方法被调用时才会实例化对象,当多个线程同时调用静态方法时会给对象赋值就会出现线程安全问题
解决线程安全问题:加双检锁
public class Singleton{
private static Singleton singleton;
public Singleton(){
}
public static Singleton getInstance(){
if(singleton==null){
rsynchronized(Singleton.class){
if(singleton==null){
singleton = new Singleton();
}
}
}
return singleton;
}
}
10.原型模式
原型模式是指用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象,允许一个对象再创建另外一个可定制的对象,无需知道如何创建的细节
浅拷贝:原对象中的引用数据类型和克隆对象中的引用数据类型是同一个对象
实现Cloneable接口
深拷贝:原对象中的引用数据类型和克隆对象中引用数据类型不是同一个对象
实现Cloneable接口重写Object中的clone方法,将权限protect变大改为public,使用对象的序列化和反序列化实现深拷贝
11.Spring中常见的注解有哪些?
-
IOC
创建Bean
@Controller:在控制层使用,标识该类是SpringMVC controller处理器。,用来创建http请求的对象
@Service:在业务逻辑层使用,用于标注业务层组件
@Respository:在数据访问层使用,用于标注数据库访问组件,dao组件
@Compoent:当组件不好归类,不属于以上三种,就可以使用这个注解进行标注
@Bean:产生一个Bean并且交给Spring容器管理
依赖注入
@Autowired:把配置好的bean拿来用,完成属性、方法的组装,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作(@Resource)
配置类
@Configuration:声明当前类为配置类
组件扫描
@ComponentScan:用于Component组件进行扫描
加载配置文件
@PropertySource:加载的是除application.yml和bootstrap.yml之外的配置文件
@Order:控制创建Bean的顺序
@RunWith:运行器,Spring中通常用于对Junit支持
@Scope:用来配置Spring Bean的作用域
@Qualifier:当创建多个具有相同类型bean时,并且想要用一个属性只为其中的一个进行装配,在这种情况下可以使用@Qualifier指定哪一个bean将会被装配,用来消除混乱
-
AOP
@Aspect:表示当前类为一个切面类
@Component:当前类交给Spring管理
@Pointcut:切点表达式定义匹配规则
@Around:环绕通知,可以在目标方法执行前后执行一些操作以及目标方法抛出异常时执行的一些操作
12.@Transactional注解,事务什么时候会失效?
-
方法不是public、没有交给Spring管理,数据库引擎不支持事务
-
将异常信息使用try-catch包裹,异常被处理,@Transactional注解不起作用,数据提交,没有回滚
-
同一个service内方法调用,当@Transactional注解标注在方法上时,事务不起作用,方法A中数据提交,方法B中数据提交但有异常没有回滚
-
同一个service内方法调用私有的方法C,当@Transactional标注在方法C上时,事务不起作用,方法A中和方法C中的数据都提交
-
不同service方法间调用,当@Transactional标注在被调用的service中的方法B(有异常)时,A方法调用B方法,方法A中的数据提交,事务对A方法不起作用,方法B回滚
-
service中方法抛出的异常不是RuntimeException而是Exception
13.SpringBoot和Spring有什么区别?
Spring:
Spring为Java应用程序提供了全面的架构支持。它提供了Spring JDBC对JDBC的简单封装、SpringMVC框架的实现,SpringAOP面向切面编程、SpringTest单元测试、SpringSecurity
整合第三方框架(Mybatis、springmvc、struts),简化API的使用(引入大量的依赖和大量的配置,xml文件配置繁琐)
SpringBoot
SpringBoot是对Spring框架的扩展
SpringBoot内嵌tomcat,提供了starter(用maven封装的一个模块)起步依赖,简化maven配置自动配置Spring,自动装配一些可能会用到的对象实例,无代码生成和xml配置,允许打包可执行jar,使用java-jar独立运行jar
14.Spring MVC的Controller线程安全吗?如何解决?
不安全。默认情况下controller是单例模式,单例模式下要保证线程安全必须使用ThreadLocal来封装变量才能保证线程安全
也可以在controller上添加@Scope注解定义IOC容器作用域,定义value属性为prototype,使controller变成多例,再没有静态成员变量的情况下是线程安全的(前提),但是如果使用了静态成员变量,那么静态成员变量的创建是随着类的加载而加载,所以无法保证线程安全,所有想要保证线程安全必须使用ThreadLocal来封装这个变量
15.Spring MVC怎么处理异常?
实现HandlerExceptionResolver接口自定义全局异常处理器
可处理处理器异常、数据绑定异常以及控制器执行时出现的异常,发生异常时Spring MVC会调用resolveException()方法,并转到ModeAndView对应的视图中,返回异常报告页面返回给前端
@ControllerAdvice+@ExceptionHandler
可实现全局异常的捕获,被@ControllerAdvice标记的类不需要将进行异常处理的方法与出错的方法在同一个Controller中,若其他的由@Controller标记的方法中出现异常,且没有在类中定义处理方法,则会去标记了@ControllerAdvice的类中去找异常处理方法,如果也找不到就会在页面抛出异常