1.悲观锁和乐观锁
悲观锁和乐观锁是两种用于解决并发场景下数据竞争问题的机制。
悲观锁基于悲观的态度,认为数据可能会被多个用户同时修改,因此在修改数据之前先将数据锁住,防止其他用户修改。悲观锁的实现方式是加锁,可以是代码块加锁(如Java的synchronized关键字)或数据加锁(如MySQL中的排它锁)。悲观锁可以保证数据的独占性和正确性,但可能导致性能开销较大。
乐观锁则基于乐观的态度,认为数据不会被多个用户同时修改,因此不会上锁,而是在执行更新时判断在此期间是否有其他用户修改了数据。如果别人修改了数据,则放弃操作,否则执行操作。乐观锁的实现方式主要有两种:CAS机制和版本号机制。
总之,悲观锁和乐观锁是两种不同的并发控制机制,各有优缺点,应根据具体情况选择合适的策略。
2. spring的注入方式
Spring的注入方式主要有以下几种:
- Setter注入:通过setter方法将值注入到bean中。这是最常见的一种注入方式。
- 构造器注入:通过构造器将值注入到bean中。这种方式需要在构造器中指定所有需要注入的属性。
- 属性注入:通过Java Beans的属性文件将值注入到bean中。这种方式需要使用Spring提供的PropertyValues和BeanWrapper来实现。
- 方法注入:通过Java Beans的方法将值注入到bean中。这种方式需要在方法中指定所有需要注入的属性。
- @Autowired注入:通过@Autowired注解将值注入到bean中。这种方式可以直接将值注入到构造器或setter方法中。
- @Resource注入:通过@Resource注解将值注入到bean中。这种方式和@Autowired类似,但是可以在bean中使用name属性指定需要注入的属性名。
- @Inject注入:通过@Inject注解将值注入到bean中。这种方式和@Autowired类似,但是可以在bean中使用name属性指定需要注入的属性名。
3. 索引失效
- like查询以“%”开头。
- or语句前后没有同时使用索引。
- 组合索引中不是使用第一列索引。
- 在索引列上使用“IS NULL”或“IS NOT NULL”操作。
- 在索引字段上使用“not”,“<>”,“! =”等等。
- 查询条件使用函数在索引列上。
- 没有查询条件,或者查询条件没有建立索引。
- 在查询条件上没有使用引导列。
- 查询的数量是大表的大部分,应该是30%以上。
- 对小表查询。
- 统计数据不真实。
- CBO计算走索引花费过大的情况。
4. 死锁的条件和解除死锁的方式
死锁的条件:
- 互斥条件:每个资源要么已经分配给一个进程,要么处于可用状态,不能同时被两个进程持有。
- 占有并等待条件:一个进程已经占有了至少一个资源,同时还有未被满足的资源请求,但该进程在继续执行之前需要等待更多的资源。
- 不可抢占条件:一个已经分配给某个进程的资源,不能强制性地被抢占,除非该进程主动释放。
- 环路等待条件:存在一系列的进程,每个进程都已经占有了下一个进程需要的资源,导致所有进程都无法继续执行。
解除死锁的方式:
- 抢占资源:从一个或多个进程中抢占足够数量的资源分配给死锁进程,以解除死锁状态。
- 终止(或撤销)进程:终止或撤销系统中的一个或多个死锁进程,直至打破死锁状态。具体做法有:
- 终止所有的死锁进程:简单粗暴,但是代价很大,很有可能会导致一些已经运行了很久的进程前功尽弃。
- 逐个终止进程,直至死锁状态解除:该方法的代价也很大,因为每终止一个进程就需要使用死锁检测来检测系统当前是否处于死锁状态。另外,每次终止进程的时候终止那个进程呢?每次都应该采用最优策略来选择一个“代价最小”的进程来解除死锁状态。
5. 线程的创建方式
线程的创建方式主要有两种,具体如下:
- 继承Thread类:自定义类MyThread继承Thread类,重写run()方法,创建线程对象,启动线程。注意,启动线程使用的是start()方法而不是run()方法,线程不能多次启动,只能执行一次。
- 实现Runnable接口:自定义类MyRunnable实现Runnable接口,重写run()方法,创建MyRunnable类的对象,创建Thread类的对象,并把步骤3创建的对象作为构造参数传递,启动线程。
此外,还有实现Callable接口和使用Executor框架创建线程池的方式,但是这些相对复杂,如有需要,可以深入研究。
6. runnable 和callable的区别
Runnable和Callable是Java中的两个接口,它们都可以用来实现多线程,但它们有一些明显的区别。
- 返回值:Runnable接口中的run方法没有返回值,而Callable接口中的call方法有返回值。
- 异常处理:Runnable接口中的run方法不能抛出异常,而Callable接口中的call方法可以抛出异常。
- 方法名:Runnable接口中的方法是run,而Callable接口中的方法是call。
- 任务执行后获取结果:Runnable接口无法直接获取任务的执行结果,而Callable接口可以调用get方法获取任务的执行结果。
- 应用场景:Runnable常常用于实现Thread的实例方法,而Callable常常用于实现FutureTask。
7. 接口和抽象的区别
接口和抽象在以下六个方面存在区别:
- 语义:抽象是隐藏对象的实现细节,只展示其对外提供的操作。而接口则定义了一个合约,规定了类应该实现哪些方法。
- 方法:抽象类可以有具体的方法,包括普通方法和受保护的方法。而接口只能包含抽象方法。
- 属性:抽象类可以包含普通属性和受保护的属性。而接口只能包含静态常量和抽象方法。
- 使用情况:抽象类主要用于类与类之间,表示一种继承关系。而接口主要用于类与类之间,或者类自身与外部之间,是一种合同的关系。
- 实现:抽象类只能被单继承,且无需使用implements关键字。而接口可以被多实现,使用implements关键字。
- 细节:抽象类可以有构造方法,但接口不能有构造方法。抽象类中的方法可以有静态方法,但接口不能有静态方法。此外,抽象类中的初始化块不能被调用,而接口中的初始化块不能被调用。
8. 内联和外联有什么区别
在MySQL中,内联和外联主要应用于连接(join)操作,区别如下:
内联(INNER JOIN):内联连接是指在查询中,只返回连接表中满足连接条件的记录。换句话说,只有当连接表中的字段与主表中的字段匹配时,才会返回这些记录。
例如:
sql复制代码
SELECT * FROM users INNER JOIN topics ON users.id = topics.user_id;
上述语句会返回users表和topics表中user_id字段匹配的所有记录,不匹配的记录会被忽略。
外联(OUTER JOIN):外联连接与内联相反,它会返回连接表中的所有记录,即使没有匹配的记录。根据外联的类型,可以分为左外联(LEFT OUTER JOIN)和右外联(RIGHT OUTER JOIN)。
左外联(LEFT OUTER JOIN):以左边的表为主表,返回左表的所有记录,以及与右表匹配的记录。如果右表中没有匹配的记录,结果中对应的字段会以NULL代替。
例如:
sql复制代码
SELECT * FROM users LEFT JOIN topics ON users.id = topics.user_id;
上述语句会返回users表中的所有记录和topics表中匹配到的记录,对于未匹配到的topics表记录,都会被返回并以NULL代替。
右外联(RIGHT OUTER JOIN):以右边的表为主表,返回右表的所有记录,以及与左表匹配的记录。如果左表中没有匹配的记录,结果中对应的字段会以NULL代替。
例如:
sql复制代码
SELECT * FROM users RIGHT JOIN topics ON users.id = topics.user_id;
上述语句会返回users表中的所有记录和topics表中匹配到的记录,对于未匹配到的users表记录,都会被返回并以NULL代替。
总的来说,内联连接只返回满足连接条件的记录,而外联连接会返回连接表中的所有记录,包括未匹配到的记录。
9. 拦截器和过滤器有什么区别
拦截器和过滤器在以下几个方面存在区别:
- 功能:拦截器可以拦截请求和响应,而过滤器只能拦截请求。
- 实现方式:拦截器是Spring提供并管理的,可以获取IOC容器中的各个bean,而过滤器是JavaEE标准,只需依赖Servlet API。
- 执行时机:拦截器在请求进入容器后但在进入servlet之前执行,而过滤器在请求进入servlet之前和响应离开servlet之后执行。
- 执行方式:拦截器可以获取IOC容器中的各个bean,因此可以使用Spring的功能,如依赖注入、AOP等。而过滤器没有这样的功能。
- 生命周期:拦截器的生命周期由IoC容器管理,而过滤器的生命周期由Servlet容器管理。
- 执行顺序:过滤器的执行顺序与它们的配置顺序有关,而拦截器的执行顺序与它们的配置顺序无关。
10. AOP的底层原理
AOP的底层原理是动态代理。动态代理是在运行时动态地创建代理类的一种机制,它可以在不修改源码的情况下,为一个对象提供额外的功能。在AOP中,通常会使用动态代理来为业务对象创建代理对象,然后在代理对象的方法调用中植入切面逻辑。这样就可以在不修改业务对象的情况下,在方法调用的前后、抛出异常的前后植入切面逻辑。
11. spring框架的特性
Spring框架具有以下特性:
- 方便解耦和简化开发:通过Spring的AOP功能,开发者可以方便地进行面向切面的编程,降低业务对象和系统服务之间的耦合度,简化开发。
- 低侵入式设计:Spring框架采用的是低侵入式设计,使得代码污染极低。
- 独立于各种应用服务器:Spring框架不依赖于任何特定的应用服务器。
- 支持各种优秀的框架:Spring框架可以降低各种优秀框架的使用难度,比如Struts、Hibernate、Quartz等。
- 方便集成测试:Spring框架支持使用非容器依赖的方式来运行所有的测试,这使得测试更为方便。
- 简化Java EE API的使用:Spring框架对Java EE API进行了封装,使得这些API的使用难度降低。
- 支持声明式事务管理:在Spring中,我们可以从繁琐的事务管理代码中解脱出来,通过声明式方式来管理事务,提高开发效率和代码质量。
- 方便集成各种优秀的技术:Spring框架可以方便地集成各种优秀的技术,比如ORM框架、远程调用框架等。
- 独立于任何特定的数据访问技术:Spring框架不限制使用特定的数据访问技术,可以方便地与第三方持久层框架集成。
- 简化单元测试的编写:Spring框架支持使用注解来测试Spring程序,比如使用@Test注解来标注测试方法。
12. HashMap是线程安全的吗
HashMap不是线程安全的。如果多个线程同时对HashMap进行操作,可能会导致数据不一致的问题。
Java提供了多种线程安全的Map实现,包括:
- ConcurrentHashMap:它是线程安全的,采用分段锁机制,不同的段(Segment)可以被不同的线程同时访问,从而提高了并发性能。
- Hashtable:它是线程安全的,但是性能相对较差,因为在每个方法上都使用了同步锁。
- Collections.synchronizedMap:它可以将非线程安全的Map转换为线程安全的Map,但是性能相对较差,因为在每个方法上都使用了同步锁。
在选择线程安全的Map时,需要根据实际情况进行选择。如果需要高并发性能,可以选择ConcurrentHashMap;如果需要完全的线程安全,可以选择Hashtable;如果需要将非线程安全的Map转换为线程安全的Map,可以选择Collections.synchronizedMap。
13. Java有哪些集合框架
Java的集合框架主要包括以下几类:
- Collection接口:这是集合框架的顶层接口,它定义了一些通用的操作,如添加元素、删除元素等。
- List接口:List接口是Collection接口的子接口,用于存储有序的、可重复的数据。List接口的主要实现类有ArrayList、LinkedList、Vector等。
- Set接口:Set接口也是Collection接口的子接口,用于存储无序的、不可重复的数据。Set接口的主要实现类有HashSet、LinkedHashSet、TreeSet等。
- Map接口:Map接口是用来存储键值对的集合,其中的每个元素都是一个键值对,键和值都可以重复。Map接口的主要实现类有HashMap、LinkedHashMap、TreeMap等。
这些集合类在Java的集合框架中各有其特点和适用场景,开发人员可以根据实际需求选择合适的集合类来使用。
14 sql的优化经验
优化SQL语句可以显著提高数据库性能和响应速度。以下是一些SQL优化经验:
- 查询的模糊匹配尽量避免使用LIKE '%parm1%',因为这会导致相关列的索引无法使用,可以考虑使用全文搜索或多字段匹配。
- 避免在查询条件中使用函数,因为这会导致相关列的索引无法使用。
- 在设计表时,尽量定义合适的索引,避免在执行查询时进行全表扫描。
- 在查询时,尽量减少使用OR,可以使用UNION或UNION ALL来代替。
- 在查询时,尽量避免在WHERE子句中使用多个条件,可以使用AND和OR来组合条件。
- 在查询时,尽量避免使用子查询,可以使用连接(JOIN)来代替。
- 在查询时,尽量避免重复查询相同的数据,可以缓存查询结果或使用临时表。
- 在查询时,尽量避免使用DISTINCT和ORDER BY,如果必须使用,可以考虑使用GROUP BY和子查询来代替。
- 在设计数据库时,尽量使用合适的数据类型,避免使用字符串类型存储数字或日期等。
- 在设计数据库时,尽量使用合适的数据表结构,避免使用大量无意义的列或重复的列。
15. mybatis的#{}和${}有什么区别
MyBatis中的#{}和${}在参数传递和防止SQL注入方面存在一些不同。
- 参数传递方式:
- #{}是预编译处理,会将参数以占位符{}的形式传递给SQL语句,然后使用PreparedStatement的set方法来为占位符设置值。因此,#{}可以防止SQL注入,并且能对绑定的参数自动进行类型转换。
- 是字符串替换,会将参数直接拼接到SQL语句中,然后直接执行。因此,{}无法防止SQL注入,且对于绑定的参数不会进行类型转换。
- SQL注入:
- 由于#{}的预编译处理,能有效防止SQL注入,因为MyBatis在处理#{}时,会将SQL中的#{}替换为?号,并通过PreparedStatement的set方法为占位符设置参数值,确保参数值安全。
- ${}由于是字符串替换,所以在拼接SQL语句时可能会直接将未经过处理的参数拼接进去,如果参数中包含了SQL注入的语句,那么就可能导致SQL注入问题的发生。
16. hashmap的底层
HashMap的底层是数组加链表。当像哈希表里面添加一个对象时,会先调用对象的hashcode算法,算出哈希码值,根据哈希算法算出对应的数组的索引值,再根据索引值查找数组,数组中是否存在对象,如果不存在对象直接存进去。如果数组中存在该对象,会调用对象的equals方法,比较key值是否相等。如果相等,value 值直接覆盖。如果不相等,则形成链表结构。
在Java 8以后,HashMap的底层结构使用了数组+链表+红黑树。具体实现起来就是一维数组和单向链表,数组的元素就是一个个Node对象。在哈希算法计算出的链表长度大于8并且数组大小大于64的时候,会将链表转换为红黑树。
17 大表如何进行查询优化
针对大表查询的优化方案,以下是一些常见的方法:
- **索引优化。**通过建立合理高效的索引,提高查询的速度。
- **SQL优化。**组织优化SQL语句,使查询效率达到最优,在很多情况下要考虑索引的作用。
- **水平拆分。**如果表中的数据呈现出某一类特性,比如呈现时间特性,那么可以根据时间段将表拆分成多个。
- **垂直拆分。**将表按字段拆分成多个表,常用的字段放在一个表,不常用的字段或大字段放在另外一个表。