2020-02-06
1.进程和线程的区别
一个程序下至少有一个进程,一个进程下至少有一个线程,一个进程下也可以有多个线程来增加程序的执行速度。
2.线程的生命周期,多线程并发会有什么影响
3.队列和栈的特性。在什么实际案例中见过?
堆栈的特性:先进后出,后进先出``队列的特性:先进先出,后进后出
4.synchroized的作用,java中还有那些常用的锁?
synchronized` `它可以把任意一个非 NULL 的对象当作锁。他属于独占式的悲观锁,同时属于可重入锁。
1``. 作用于方法时,锁住的是对象的实例(``this``);
2``. 当作用于静态方法时,锁住的是Class实例, 会锁所有调用该方法的线程;
3``. ``synchronized` `作用于一个对象实例时,锁住的是所有以该对象为锁的代码块。
Java中常见的锁还有乐观锁、悲观锁、自旋锁
5.synchronized 和 volatile 的区别是什么?
1``.``volatile``本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取; ``synchronized``则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
2``.``volatile``仅能使用在变量级别;``synchronized``则可以使用在变量、方法、和类级别的
3``.``volatile``仅能实现变量的修改可见性,不能保证原子性;而``synchronized``则可以保证变量的修改可见性和原子性
4``.``volatile``不会造成线程的阻塞;``synchronized``可能会造成线程的阻塞。
5``.``volatile``标记的变量不会被编译器优化;``synchronized``标记的变量可以被编译器优化
6.spring的Bean的作用域和生命周期
Bean的作用域:单例(singleton)、原型(prototype)、请求(request)、会话(session)、全局会话(global session)
Bean的生命周期流程:
1``.首先容器启动后,会对scope为singleton且非懒加载的bean进行实例化,
2``.按照Bean定义信息配置信息,注入所有的属性,
3``.如果Bean实现了BeanNameAware接口,会回调该接口的setBeanName()方法,传入该Bean的id,此时该Bean就获得了自己在配置文件中的id,
4``.如果Bean实现了BeanFactoryAware接口,会回调该接口的setBeanFactory()方法,传入该Bean的BeanFactory,这样该Bean就获得了自己所在的BeanFactory,
5``.如果Bean实现了ApplicationContextAware接口,会回调该接口的setApplicationContext()方法,传入该Bean的ApplicationContext,这样该Bean就获得了自己所在的ApplicationContext,
6``.如果有Bean实现了BeanPostProcessor接口,则会回调该接口的postProcessBeforeInitialzation()方法,
7``.如果Bean实现了InitializingBean接口,则会回调该接口的afterPropertiesSet()方法,
8``.如果Bean配置了init-method方法,则会执行init-method配置的方法,
9``.如果有Bean实现了BeanPostProcessor接口,则会回调该接口的postProcessAfterInitialization()方法,
10``.经过流程``9``之后,就可以正式使用该Bean了,对于scope为singleton的Bean,Spring的ioc容器中会缓存一份该bean的实例,而对于scope为prototype的Bean,
``每次被调用都会``new``一个新的对象,期生命周期就交给调用方管理了,不再是Spring容器进行管理了
11``.容器关闭后,如果Bean实现了DisposableBean接口,则会回调该接口的destroy()方法,
12``.如果Bean配置了destroy-method方法,则会执行destroy-method配置的方法,至此,整个Bean的生命周期结束。
7.spring常用的六个注解以及作用
@Component``(value)
配置类,当使用该注解时,SpringIOC会将这个类自动扫描成一个bean实例
不写的时候,默认是类名,且首字母小写
@Autowired
自动装配,会根据类型寻找指定的Bean,required:表示假如说在bean目录下找不到指定的注解还需不需要注入,默认是``true` `修改为``false``时,表示假如说没有找到可以允许不注入。
@ImportResource``({``"classpath:xxx.xml"``})
能够读取多个xml文件,这样就可以引入多个xml定义的bean
@Service
标注于业务层组件上表示定义一个bean,自动根据所标注的组件名称实例化一个首字母为小写的bean。
@RequestBody
用来传对象参数
@Resource
用来自动装配Bean,激活一个命名资源的依赖注入。``@Resource``属性name可以定义被自动装配Bean的名称
8.解释什么是事务,多线程事务并发会产生哪些问题,简述事务的四种隔离性
事务是应用程序中一系列严密的操作,所有操作必须成功完成,否则在每个操作中所作的所有更改都会被撤消。
也就是事务具有原子性,一个事务中的一系列的操作要么全部成功,要么一个都不做。
事务的结束有两种,当事务中的所以步骤全部成功执行时,事务提交。如果其中一个步骤失败,将发生回滚操作,撤消撤消之前到事务开始时的所以操作。
并发问题可归纳为以下几类:
A.丢失更新
B.脏读
C.不可重复读
D. 覆盖更新
E.虚读(幻读)
事务的四种隔离性:
A.Serializable(串行化):一个事务在执行过程中完全看不到其他事务对数据库所做的更新
(事务执行的时候不允许别的事务并发执行。事务串行化执行,事务只能一个接着一个地执行,而不能并发执行。)。
B.Repeatable Read(可重复读):一个事务在执行过程中可以看到其他事务已经提交的新插入的记录,但是不能看到其他其他事务对已有记录的更新。
C.Read Commited(读已提交数据):一个事务在执行过程中可以看到其他事务已经提交的新插入的记录,而且能看到其他事务已经提交的对已有记录的更新。
D.Read Uncommitted(读未提交数据):一个事务在执行过程中可以看到其他事务没有提交的新插入的记录,而且能看到其他事务没有提交的对已有记录的更新。
9.什么是aop,spring的几种通知方式
aop是面向切面编程,在整个系统中,动态横向添加新的功能还不改变原来的业务流程的编程
spring的几种通知方式:
1``.前置通知(方法调用前通知)
2``.后置通知(方法调用后正常返回的时候通知,可以获取返回值,发生异常的时候不会执行)
3``.最终通知(无论程序是否发生异常都会执行的)
4``.异常通知(发生异常的执行)
5``.环绕通知(方法执行的时候执行)
10.画出jvm的内存运行图并解释其作用
首先Java源代码文件(.java后缀)会被Java编译器编译为字节码文件(.``class``后缀),
然后由JVM中的类加载器加载各个类的字节码文件,加载完毕之后,交由JVM执行引擎执行。
在整个程序执行过程中,JVM会用一段空间来存储程序执行期间需要用到的数据和相关信息,
这段空间一般被称作为Runtime Data Area(运行时数据区),也就是我们常说的JVM内存。
因此,在Java中我们常常说到的内存管理就是针对这段空间进行管理
11.手写快速排序算法
StreamApi
第一种排序:sorted(),自然排序
@Test
public void test8() {
List list = Arrays.asList("aaa","bbb","ccc","ddd");
list.stream().sorted().forEach(System.out::println);
}
第二种排序:sorted(Comparator com)-定制排序
public void test8() {
List list = Arrays.asList("qqq","aaa","bbb","ccc","ddd");
list.stream().sorted().forEach(System.out::println);
deptList.stream().sorted((x,y) -> {
if(x.getDeptno().equals(y.getDeptno())) {
return x.getDname().compareTo(y.getDname());
}else {
return x.getLoc().compareTo(y.getLoc());
}
});
}
// 第三种按Map排序
//创建一个Map,并填入数据
Map codes = new HashMap<>();
codes.put("United States", 1);
codes.put("Germany", 49);
codes.put("France", 33);
codes.put("China", 86);
codes.put("Pakistan", 92);
// 按照Map的键进行排序
Map sortedMap = codes.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.collect(
Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(oldVal, newVal) -> oldVal,
LinkedHashMap::``new
)
);
// 将排序后的Map打印
sortedMap.entrySet().forEach(System.out::println);
//按照value排序
Map sortedMap2 = codes.entrySet().stream()
.sorted(Map.Entry.comparingByValue())
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(oldVal, newVal) -> oldVal,
LinkedHashMap::``new``));
//排序以后打印
sortedMap2.entrySet().forEach(System.out::println);
12.手写线程安全的单例模式
//懒汉式,线程安全 public class Singleton { private static Singleton instance; private Singleton (){} public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
//饿汉式 public class Singleton { private static Singleton instance = new Singleton(); private Singleton (){} public static Singleton getInstance() { return instance; } }
//双检锁/双重校验锁(DCL,即double-checked locking),JDK1.5起 public class Singleton { private volatile static Singleton singleton; private Singleton (){} public staitic Singleton getSingleton() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return singleton; } }
//登记式/静态内部类 public class Singleton { private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } private Singleton (){} public static final Singleton getInstance() { return SingletonHolder.INSTANCE; } }
//枚举,JDK1.5起 public enum Singleton { INSTANCE; public void whateverMethod() { } }
经验之谈:一般情况下,不建议使用懒汉方式,建议使用饿汉方式。只有在要明确实现lazy loading 效果时,才会 使用登记方式。如果涉及到反序列化创建对象时,可以尝试使用枚举方式。如果有其他特殊的需求,可以考虑使用双检 锁方式
13.设计一个秒杀的结构图