共计 9198 个字符,预计需要花费 23 分钟才能阅读完成。
并发编程、数据库和 Spring,我认为这个算程序员开发面试的都必须接触到的。作为面试的热点及难点,始终深受面试官青眼都是离不开的。明天就聊一聊一些经典的题目,让大家对 Spring、MySQL、并发有肯定分明的认知。每天看一看,开发不艰难。
一、并发编程
另外自己整顿了 20 年面试题大全,蕴含 spring、并发、数据库、Redis、分布式、dubbo、JVM、微服务等方面总结,下图是局部截图,需要的话关注公众号【Java 斗帝】回复“666”收费获取。
1、Java 中实现多线程有几种办法
(1)继承 Thread 类;
(2)实现 Runnable 接口;
(3)实现 Callable 接口通过 FutureTask 包装器来创立 Thread 线程;
(4)应用 ExecutorService、Callable、Future 实现有返回后果的多线程(也就是应用了 ExecutorService 来治理后面的三种形式)。
2、如何进行一个正在运行的线程
(1)应用退出标记,使线程失常退出,也就是当 run 办法实现后线程终止。
(2)应用 stop 办法强行终止,然而不举荐这个办法,因为 stop 和 suspend 及 resume 一样都是过期作废的办法。
(3)应用 interrupt 办法中断线程。
class MyThread extends Thread {
volatile Boolean stop = false;
public void run() {while (!stop) {System.out.println(getName() + "is running");
try {sleep(1000);
}
catch (InterruptedException e) {System.out.println("week up from blcok...");
stop = true;
// 在异样解决代码中批改共享变量的状态
}
}
System.out.println(getName() + "is exiting...");
}
}
class InterruptThreadDemo3 {public static void main(String[] args) throws InterruptedException {MyThread m1 = new MyThread();
System.out.println("Starting thread...");
m1.start();
Thread.sleep(3000);
m1.interrupt();
// 阻塞时退出阻塞状态
Thread.sleep(3000);
// 主线程休眠 3 秒以便察看线程 m1 的中断状况
System.out.println("Stopping application...");
}
}
3、notify()和 notifyAll()有什么区别?
notify 可能会导致死锁,而 notifyAll 则不会
任何时候只有一个线程能够取得锁,也就是说只有一个线程能够运行 synchronized 中的代码应用 notifyall, 能够唤醒所有处于 wait 状态的线程,使其从新进入锁的抢夺队列中,而 notify 只能唤醒一个。
wait() 应配合 while 循环应用,不应应用 if,务必在 wait()调用前后都查看条件,如果不满足,必须调用 notify()唤醒另外的线程来解决,本人持续 wait()直至条件满足再往下执行。
notify() 是对 notifyAll()的一个优化,但它有很准确的利用场景,并且要求正确应用。不然可能导致死锁。正确的场景应该是 WaitSet 中期待的是雷同的条件,唤醒任一个都能正确处理接下来的事项,如果唤醒的线程无奈正确处理,务必确保持续 notify()下一个线程,并且本身须要从新回到 WaitSet 中。
4、sleep()和 wait() 有什么区别?
对于 sleep()办法,咱们首先要晓得该办法是属于 Thread 类中的。而 wait()办法,则是属于 Object 类中
的。
sleep()办法导致了程序暂停执行指定的工夫,让出 cpu 该其余线程,然而他的监控状态仍然保持者,当指定的工夫到了又会主动复原运行状态。在调用 sleep()办法的过程中,线程不会开释对象锁。
当调用 wait()办法的时候,线程会放弃对象锁,进入期待此对象的期待锁定池,只有针对此对象调用 notify()办法后本线程才进入对象锁定池筹备,获取对象锁进入运行状态。
5、volatile 是什么? 能够保障有序性吗?
一旦一个共享变量(类的成员变量、类的动态成员变量)被 volatile 润饰之后,那么就具备了两层语义:
(1)保障了不同线程对这个变量进行操作时的可见性,即一个线程批改了某个变量的值,这新值对其余线程来说是立刻可见的,volatile 关键字会强制将批改的值立刻写入主存。
(2)禁止进行指令重排序。
volatile 不是原子性操作
什么叫保障局部有序性?
当程序执行到 volatile 变量的读操作或者写操作时,在其后面的操作的更改必定全副曾经进行,且后果曾经对前面的操作可见;在其前面的操作必定还没有进行;
x = 2;// 语句 1
y = 0;// 语句 2
flag = true;// 语句 3
x = 4;// 语句 4
y = -1;// 语句 5
因为flag 变量为 volatile 变量,那么在进行指令重排序的过程的时候,不会将语句 3 放到语句 1、语句 2 后面,也不会讲语句 3 放到语句 4、语句 5 前面。然而要留神语句 1 和语句 2 的程序、语句 4 和语句 5 的程序是不作任何保障的。
应用 Volatile 个别用于 状态标记量 和 单例模式的双检锁
6、Thread 类中的 start() 和 run() 办法有什么区别?
start()办法被用来启动新创建的线程,而且 start()外部调用了 run()办法,这和间接调用 run()办法的成果不一样。当你调用 run()办法的时候,只会是在原来的线程中调用,没有新的线程启动,start()办法才会启动新线程。
7、为什么 wait, notify 和 notifyAll 这些办法不在 thread 类外面?
显著的起因是 JAVA 提供的锁是对象级的而不是线程级的,每个对象都有锁,通过线程取得。如果线程须要期待某些锁那么调用对象中的 wait()办法就有意义了。如果 wait()办法定义在 Thread 类中,线程正在期待的是哪个锁就不显著了。简略的说,因为 wait,notify 和 notifyAll 都是锁级别的操作,所以把他们定义在 Object 类中因为锁属于对象。
8、为什么 wait 和 notify 办法要在同步块中调用?
(1)只有在调用线程领有某个对象的独占锁时,才可能调用该对象的 wait(),notify()和 notifyAll()办法。
(2)如果你不这么做,你的代码会抛出 IllegalMonitorStateException 异样。
(3)还有一个起因是为了防止 wait 和 notify 之间产生竞态条件。
wait()办法强制以后线程开释对象锁。这意味着在调用某对象的 wait()办法之前,以后线程必须曾经取得该对象的锁。因而,线程必须在某个对象的同步办法或同步代码块中能力调用该对象的 wait()办法。
在调用对象的 notify()和 notifyAll()办法之前,调用线程必须曾经失去该对象的锁。因而,必须在某个对象的同步办法或同步代码块中能力调用该对象的 notify()或 notifyAll()办法。
调用 wait()办法的起因通常是,调用线程心愿某个非凡的状态 (或变量) 被设置之后再继续执行。调用 notify()或 notifyAll()办法的起因通常是,调用线程心愿通知其余期待中的线程:“非凡状态曾经被设置”。这个状态作为线程间通信的通道,它必须是一个可变的共享状态(或变量)。
9、Java 中 interrupted 和 isInterruptedd 办法的区别?
interrupted() 和 isInterrupted()的次要区别是前者会将中断状态革除而后者不会。Java 多线程的中断机制是用外部标识来实现的,调用 Thread.interrupt()来中断一个线程就会设置中断标识为 true。当中断线程调用静态方法 Thread.interrupted()来查看中断状态时,中断状态会被清零。而非静态方法 isInterrupted()用来查问其它线程的中断状态且不会扭转中断状态标识。简略的说就是任何抛出 InterruptedException 异样的办法都会将中断状态清零。无论如何,一个线程的中断状态有有可能被其它线程调用中断来扭转。
10、Java 中 synchronized 和 ReentrantLock 有什么不同?
类似点:
这两种同步形式有很多相似之处,它们都是加锁形式同步,而且都是阻塞式的同步,也就是说当如果一个线程取得了对象锁,进入了同步块,其余拜访该同步块的线程都必须阻塞在同步块里面期待,而进行线程阻塞和唤醒的代价是比拟高的。
区别:
这两种形式最大区别就是对于 Synchronized 来说,它是 java 语言的关键字,是原生语法层面的互斥,须要 jvm 实现。而 ReentrantLock 它是 JDK 1.5 之后提供的 API 层面的互斥锁,须要 lock()和 unlock()办法配合 try/finally 语句块来实现。
Synchronized 进过编译,会在同步块的前后别离造成 monitorenter 和 monitorexit 这个两个字节码指令。在执行 monitorenter 指令时,首先要尝试获取对象锁。如果这个对象没被锁定,或者以后线程曾经领有了那个对象锁,把锁的计算器加 1,相应的,在执行 monitorexit 指令时会将锁计算器就减 1,当计算器为 0 时,锁就被开释了。如果获取对象锁失败,那以后线程就要阻塞,直到对象锁被另一个线程开释为止。
因为 ReentrantLock 是 java.util.concurrent 包下提供的一套互斥锁,相比 Synchronized,ReentrantLock 类提供了一些高级性能,次要有以下 3 项:
(1)期待可中断,持有锁的线程长期不开释的时候,正在期待的线程能够抉择放弃期待,这相当于 Synchronized 来说能够避免出现死锁的状况。
(2)偏心锁,多个线程期待同一个锁时,必须依照申请锁的工夫程序取得锁,Synchronized 锁非偏心锁,ReentrantLock 默认的构造函数是创立的非偏心锁,能够通过参数 true 设为偏心锁,但偏心锁体现的性能不是很好。
(3)锁绑定多个条件,一个 ReentrantLock 对象能够同时绑定对个对象。
二、MySQL
1.Mysql 中有哪几种锁?
(1)表级锁:开销小,加锁快。不会呈现死锁,锁定粒度大,产生锁抵触的概率高,并发度低。
(2)行级锁:开销大,加锁慢。会呈现死锁,锁定粒度小,产生锁抵触的概率低,并发度高。
(3)页面锁:开销工夫、加锁工夫、锁定粒度在 表级锁 与 行级锁 之间,会呈现死锁,并发度中等。
2.CHAR 与 VARCHAR 的区别?
(1)CHAR 长度不可变,范畴 1~255。若存储长度未达到定义的长度,则以 空格 填充。存取速度快,但容易节约空间。
(2)VARCHAR 长度可变,范畴 1~65535。若存储长度未达到定义的长度,则存理论长度数据。存取速度稍慢,但节约空间。
3. 能说下 myisam 和 innodb 的区别吗?
myisam 引擎是 5.1 版本之前的默认引擎,反对全文检索、压缩、空间函数等,然而不反对事务和行级锁,所以个别用于有大量查问大量插入的场景来应用,而且 myisam 不反对外键,并且索引和数据是离开存储的。
innodb 是基于聚簇索引建设的,和 myisam 相同它反对事务、外键,并且通过 MVCC 来反对高并发,索引和数据存储在一起。
4. 你能说下事务的根本个性和隔离级别吗?
事务:数据库中,对数据的一系列操作能够看成一个整体,称为事务。这个整体要么全副执行、要么全副不执行。
ACID 属性的存在确保了 事务的牢靠。
(1)Actomicity(原子性):原子性要求 事务中的操作要么全副实现,要么回退成之前未操作的状态。即事务中某个操作失败后,会相当于什么都没产生,不会呈现改了局部数据的状况。
(2)Consistency(一致性):一致性要求 事务执行前后,数据库的状态统一,即从一个一致性状态切换到另一个一致性的状态。
(3)Isolation(隔离性):隔离性要求 并发的事务互相隔离、不可见。即一个事务看不见另一个事务外部的操作以及操作的数据。
(4)Durability(持久性):持久性要求 事务对数据库数据的批改是永恒的。即数据一旦批改提交后,其状态将永恒不变。
5. 并发问题 – 脏读、不可反复读、幻读?
对于同时运行的多个事务,若这些事务拜访同一数据时,没有采纳必要的隔离机制,则会造成如下的并发问题。
_(1)脏读:脏读 指的是当一个事务正在拜访某数据,并对这个数据进行的批改,且这条数据还未提交到数据库中,此时若另一个事务也拜访到这条数据,获取到的是这条被批改的数据,此时失去的数据不对,即脏读。
比方:tom 年龄为 22,事务 A 批改 tom 年龄为 30,此时还未提交到数据库,此时事务 B 获取 tom 年龄,失去的是 30,事务 A 回滚数据,数据库的数据仍旧是 22,但事务 B 拿到的数据是 30,这就是脏读,读错了数据。_
(2)不可反复读:指一个事务,屡次读取同一条数据,在这个事务还未完结时,另一个事务也拜访该数据并对其批改,那么可能造成事务屡次读取的数据不统一,即不可反复读。
比方:tom 年龄为 22,事务 A 读取 tom 年龄为 22,事务未完结。此时事务 B 批改 tom 年龄为 30,并提交到数据库,当事务 A 再次读取 tom 年龄为 30,事务 A 两次读取的数据不统一,即不可反复读。
(3)幻读:指事务并不是独立执行时产生的景象。一个事务批改某个表,波及表的所有行,同时另一个事务也批改表,比方减少或删除一条数据。此时第一个事务发现多出或者少了一条数据。这种状况就是幻读。
比方:事务 A 查问以后表的数据总数为 11,此时事务 B 向表中插入一条数据,事务 A 再次查问以后表数据总数为 12,即幻读。
注:
不可反复读、幻读了解起来有些相似。
不可反复读是对一条数据操作,重点在于批改某条数据。
幻读是对表进行操作,重点在于新增或删除某条数据。
6. 事务的隔离级别?
数据库系统必须具备隔离并发运行的事务的能力,使各事务间不会相互影响,防止并发问题。
隔离级别:指的是一个事务与其余事务的隔离水平。隔离级别越高,则并发能力越弱。
(1)Read Uncommitted(读未提交):即读取到 未提交的内容。
个别不应用。此隔离级别下,查问不会加锁,即可能存在两个事务操作同一个表的状况。可能会导致“脏读”、“不可反复读”、“幻读”。
(2)Read Committed(读提交):即只能读取到 已提交的内容。
罕用(oracle、SQL Server 默认隔离级别)。此隔离级别下,查问采纳 快照读 的机制,即不会读取到未提交的数据,从而防止“脏读”,然而仍可能导致“不可反复读”、“幻读”。
(3)Repeatable Read(可反复读)
罕用(mysql 默认隔离级别)。此隔离级别下,查问采纳 快照读 的机制,且事务启动后,以后数据不能被批改,从而能够防止“不可反复读”,然而仍可能导致“幻读”(新增或删除某条数据)。
(4)Serializable(串行化)
个别不应用。此隔离级别下,事务会串行化执行(排队执行),执行效率差、开销大。能够防止“脏读”、“不可反复读”、“幻读“。
【举例:】select @@transaction_isolation; -- 用于查看以后数据库的隔离级别(8.0 版本)set session transaction isolation level read committed; -- 用于设置隔离级别为 read committed
7. 说说自增主键、UUID?
(1)自增主键,数据在物理构造上是顺序存储,性能好,占用空间小。能够是 int 和 bigint 类型。int 4 字节,bigint 8 字节,我的项目中实践不应呈现 自增主键达到最大值的状况,因为数据太大,效率会大大降低,当呈现肯定的数据量后,应进行分库分表操作。
(2)UUID,数据在物理构造上是随机存储,性能较差,占用空间大。惟一 ID,绝不抵触。
三、Spring
1. 什么是 Spring IOC 容器?
Spring 框架的外围是 Spring 容器。容器创建对象,将它们拆卸在一起,配置它们并治理它们的残缺生命周期。Spring 容器应用依赖注入来治理组成应用程序的组件。容器通过读取提供的配置元数据来接管对象进行实例化,配置和组装的指令。该元数据能够通过 XML,Java 注解或 Java 代码提供。
2. 什么是依赖注入?
在依赖注入中,您不用创建对象,但必须形容如何创立它们。您不是间接在代码中将组件和服务连贯在一起,而是形容配置文件中哪些组件须要哪些服务。由 IoC 容器将它们拆卸在一起。
3. 能够通过多少种形式实现依赖注入?
通常,依赖注入能够通过三种形式实现,即:
构造函数注入
setter 注入
接口注入
在 Spring Framework 中,仅应用构造函数和 setter 注入。
4. 辨别构造函数注入和 setter 注入。
5. spring 中有多少种 IOC 容器?
BeanFactory – BeanFactory 就像一个蕴含 bean 汇合的工厂类。它会在客户端要求时实例化 bean。
ApplicationContext – ApplicationContext 接口扩大了 BeanFactory 接口。它在 BeanFactory 根底上提供了一些额定的性能。
6. 辨别 BeanFactory 和 ApplicationContext。
8. 什么是 spring bean?
它们是形成用户应用程序骨干的对象。
Bean 由 Spring IoC 容器治理。
它们由 Spring IoC 容器实例化,配置,拆卸和治理。
Bean 是基于用户提供给容器的配置元数据创立。
- spring 提供了哪些配置形式?
基于 xml 配置
bean 所需的依赖项和服务在 XML 格局的配置文件中指定。这些配置文件通常蕴含许多 bean 定义和特定于应用程序的配置选项。它们通常以 bean 标签结尾。例如:
<bean id="studentbean" class="org.edureka.firstSpring.StudentBean">
<property name="name" value="Edureka"></property>
</bean>
基于注解配置
您能够通过在相干的类,办法或字段申明上应用注解,将 bean 配置为组件类自身,而不是应用 XML 来形容 bean 拆卸。默认状况下,Spring 容器中未关上注解拆卸。因而,您须要在应用它之前在 Spring 配置文件中启用它。例如:
<beans>
<context:annotation-config/>
<!-- bean definitions go here -->
</beans>
基于 Java API 配置
Spring 的 Java 配置是通过应用 @Bean 和 @Configuration 来实现。
2.1.@Bean 注解表演与 元素雷同的角色。
2.2. @Configuration 类容许通过简略地调用同一个类中的其余 @Bean 办法来定义 bean 间依赖关系。
例如:
public class StudentConfig {
@Bean
public StudentBean myStudent() {return new StudentBean();
}
}
10. 什么是 AOP?
AOP(Aspect-Oriented Programming), 即 面向切面编程, 它与 OOP(Object-Oriented Programming, 面向对象编程) 相辅相成, 提供了与 OOP 不同的形象软件结构的视角. 在 OOP 中, 咱们以类 (class) 作为咱们的根本单元, 而 AOP 中的根本单元是 Aspect(切面)
- AOP 中的 Aspect、Advice、Pointcut、JointPoint 和 Advice 参数别离是什么?
- Aspect – Aspect 是一个实现穿插问题的类,例如事务管理。方面能够是配置的一般类,而后在 Spring Bean 配置文件中配置,或者咱们能够应用 Spring AspectJ 反对应用 @Aspect 注解将类申明为 Aspect。
- Advice – Advice 是针对特定 JoinPoint 采取的操作。在编程方面,它们是在应用程序中达到具备匹配切入点的特定 JoinPoint 时执行的办法。您能够将 Advice 视为 Spring 拦截器(Interceptor)或 Servlet 过滤器(filter)。
- Advice Arguments – 咱们能够在 advice 办法中传递参数。咱们能够在切入点中应用 args()表达式来利用于与参数模式匹配的任何办法。如果咱们应用它,那么咱们须要在确定参数类型的 advice 办法中应用雷同的名称。
- Pointcut – Pointcut 是与 JoinPoint 匹配的正则表达式,用于确定是否须要执行 Advice。Pointcut 应用与 JoinPoint 匹配的不同类型的表达式。Spring 框架应用 AspectJ Pointcut 表达式语言来确定将利用告诉办法的 JoinPoint。
- JoinPoint – JoinPoint 是应用程序中的特定点,例如办法执行,异样解决,更改对象变量值等。在 Spring AOP 中,JoinPoint 始终是办法的执行器。
12. AOP 有哪些实现形式?
实现 AOP 的技术,次要分为两大类:
动态代理 – 指应用 AOP 框架提供的命令进行编译,从而在编译阶段就可生成 AOP 代理类,因而也称为编译时加强;编译时编织(非凡编译器实现)类加载时编织(非凡的类加载器实现)。
动静代理 – 在运行时在内存中“长期”生成 AOP 动静代理类,因而也被称为运行时加强。JDK 动静代理 CGLIB
13. Spring AOP and AspectJ AOP 有什么区别?
Spring AOP 基于动静代理形式实现;AspectJ 基于动态代理形式实现。Spring AOP 仅反对办法级别的 PointCut;提供了齐全的 AOP 反对,它还反对属性级别的 PointCut。
五、最初:
针对最近很多人都在面试,我这边也整顿了相当多的面试专题材料,也有其余大厂的面经。心愿能够帮忙到大家。
上面的面试题答案都整顿成文档笔记。也还整顿了一些面试材料 & 最新 2020 收集的一些大厂的面试真题(都整顿成文档,小局部截图),须要的敌人关注公众号【Java 斗帝】回复“666”收费获取。
看完三件事❤️
如果你感觉这篇内容对你还蛮有帮忙,我想邀请你帮我三个小忙:
点赞,转发,有你们的『点赞和评论』,才是我发明的能源。
关注公众号『Java 斗帝』,不定期分享原创常识。
同时能够期待后续文章 ing????