面试题目总结(一)

1. 谈谈数据库的乐观锁和悲观锁

乐观锁和悲观锁是数据库并发控制中常用的两种策略,用于处理多个事务同时访问和修改同一个数据时的并发冲突问题。

  • 数据库的乐观锁是指在读取数据时,不对数据进行加锁,而是在更新数据时检查数据版本是否发生变化,如果没有变化则更新成功,否则更新失败。乐观锁的优点是并发性能好,因为多个事物可以同时访问同一行数据,不会阻塞其他事物完成操作。但乐观锁也有缺点,即需要维护版本号或时间戳等字段,增加了系统复杂度。
  • 悲观锁是指在读取数据时,认为数据会被其他事物修改,因此对数据进行加锁,直到当前事物完成操作并释放锁。悲观锁的缺点是会导致并发性能降低,因此多个事物不能访问同一行数据,会阻塞等待其他事务完成操作。

选择何种锁机制,需要根据具体的业务情况和性能需求来决定。一般来说,对于读多写少的场景,可以采用乐观锁;对于写多读少的场景,可以采用悲观锁。

2. spring中有哪些事务管理,你在项目中常用的事务管理

  1. 编程式事务管理
    编程式事物管理是通过编写代码来显示地管理事务的开启、提交、回滚和关闭。在代码中使用事务管理器(TransactionManager)或者直接使用(PlatformTransactionManager)接口进行事务管理,手动开启、提交或回滚事物,并定义事务的边界。这种方式灵活性较高,但需要手动处理事务的开启、提交和回滚,代码相对复杂。
  2. 声明式事务管理(XML配置)
    通过在XML配置文件中声明事务切面(TransactionAspect),使用tx:advice来定义事务的传播行为、隔离级别、回滚规则等。实现方式示例:
xml 复制代码
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="save*" propagation="REQUIRED" rollback-for="Exception" />
        <tx:method name="*" propagation="REQUIRED" />
    </tx:attributes>
</tx:advice>

<aop:config>
    <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.example.*.*(..))" />
</aop:config>
  1. 注解驱动的事务管理:
    @Transactional注解可以用来标注在方法(必须是public方法上才能生效)或类级别上,用来声明一个或多个方法需要进行事务管理。该注解具有一系列的属性,用于控制事务的传播行为、隔离级别、超时时间和只读标识等。它是基于AOP的实现

实现方式示例:

java 复制代码
@Configuration
@EnableTransactionManagement
public class AppConfig {
    // 配置数据源、实体管理器等相关Bean
}

@Service
public class MyService {
    
    @Autowired
    private MyRepository myRepository;
    
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void saveData(Data data) {
        // 执行数据库操作或其他业务逻辑
        myRepository.save(data);
    }
}

3. Set集合是怎么实现去除重复元素的

Set 集合底层是通过 Map 集合的 key 储存元素(value是一个固定的值),这就保证了Set 不重复。比如 Set 中的 HashSet。

  1. HashSet:HashSet基于哈希表实现,它使用哈希函数对元素进行快速查找。当向HashSet中添加元素时,HashSet会计算元素的哈希码,并将元素存储在相应的哈希桶中。如果哈希桶中已经存在相同的元素(根据equals()方法判断),则新元素不会被添加进去,从而实现了去重复的效果。

  2. TreeSet:TreeSet基于红黑树(一种自平衡二叉查找树)实现,它可以对元素进行排序。当向TreeSet中添加元素时,每个元素会被插入到正确的位置以保持元素的顺序。在插入元素的过程中,TreeSet会使用compareTo()或compare()方法来比较元素的大小,并根据比较结果决定元素的插入位置。如果插入的元素已经存在于TreeSet中,则新元素不会被添加进去。

  3. LinkedHashSet:LinkedHashSet是HashSet的一个子类,它通过哈希表和链表的结合来实现。它具有HashSet的去重复功能,并且可以保持元素的插入顺序。当向LinkedHashSet中添加元素时,元素会先根据哈希码存储在相应的哈希桶中,然后在哈希桶内使用链表连接起来。这样既能提供快速的查找性能,又能保持元素的插入顺序。

无论使用哪种Set实现类,它们都依赖于元素的hashCode()方法和equals()方法来进行元素的比较和去重。因此,在使用Set集合时,需要确保元素正确实现了hashCode()和equals()方法,以便正确地判断元素是否重复。

需要注意的是,Set集合中的元素必须是唯一的,因此在自定义对象作为元素时,需要正确实现hashCode()和equals()方法,以避免出现意外的重复元素。

4. 如何给5个Dog对象排序。

要给5个Dog对象排序,需要指定排序的条件和排序规则。下面介绍两种常用的排序方法:

  1. 实现Comparable接口,重写compareTo() 方法
    • Comparable接口是Java中定义的一个用于排序的接口,如果一个类实现了Comparable接口,就可以使用Collections.sort()方法对该类进行排序。
    • 要实现Comparable接口,需要在类中定义一个compareTo()方法,用来指定对象的排序规则。例如,假设Dog类有一个属性age,我们可以按照age从小到大进行排序,可以这样实现compareTo()方法:
java 复制代码
public class Dog implements Comparable<Dog> {
    private int age;
    // getter和setter方法省略
    public int compareTo(Dog otherDog) {
        return this.age - otherDog.getAge();
    }
}
  1. 实现Comparator接口,实现 compare() 方法
    • 如果无法修改Dog类,或者希望根据不同的条件进行排序,可以实现Comparator接口,它允许我们定义多个排序规则。
    • 要实现Comparator接口,需要在另一个类中定义一个compare()方法,用来指定对象的排序规则。例如,假设我们想按照名字进行排序,可以这样实现compare()方法:
java 复制代码
public class DogNameComparator implements Comparator<Dog> {
    public int compare(Dog dog1, Dog dog2) {
        return dog1.getName().compareTo(dog2.getName());
    }
}

然后,在使用排序时,可以选择使用compareTo()方法或者compare()方法进行比较,例如:

java 复制代码
List<Dog> dogs = new ArrayList<>();
dogs.add(new Dog("Charlie", 5));
dogs.add(new Dog("Buddy", 3));
dogs.add(new Dog("Max", 7));
dogs.add(new Dog("Rocky", 2));
dogs.add(new Dog("Jack", 4));

// 使用Comparable接口排序
Collections.sort(dogs);

// 使用Comparator接口排序
Collections.sort(dogs, new DogNameComparator());

以上代码演示了如何对5个Dog对象按照age或者name属性进行排序。

5. JVM的调优策略

在进行JVM调优时,可以采取以下策略来改善应用程序的性能和吞吐量:

  1. 调整堆内存大小:

    • 堆内存是JVM中用于存储对象实例的区域,通过调整堆内存大小可以提高应用程序的性能。
    • 如果应用程序频繁触发垃圾回收或者出现内存溢出问题,可以增加堆内存大小。可以通过设置-Xmx参数增加堆内存的最大值,例如-Xmx4g表示将堆内存的最大值设置为4GB。
  2. 调整垃圾回收器:

    • 垃圾回收器是负责回收无用对象的组件,在不同的场景下选择合适的垃圾回收器可以提高应用程序的性能。
    • 可以使用不同类型的垃圾回收器,如Serial、Parallel、CMS、G1等,根据应用程序的特点和需求来选择适合的垃圾回收器。
    • 可以通过设置-XX:+UseSerialGC-XX:+UseParallelGC等参数选择使用特定的垃圾回收器。
  3. 调整垃圾回收策略:

    • 垃圾回收策略包括新生代和老年代的分配比例、垃圾回收的频率等,在不同的场景下可以调整这些策略以提高性能。
    • 可以通过设置-XX:NewRatio参数调整新生代和老年代的大小比例,默认为2,表示新生代大小为老年代的1/3。
    • 可以通过设置-XX:SurvivorRatio参数调整Eden区和Survivor区的大小比例,默认为8,表示Eden区大小为Survivor区的8倍。
  4. 使用并行处理:

    • JVM提供了一些并行处理的选项,可以在多核系统上充分利用硬件资源来提高应用程序的性能。
    • 可以通过设置-XX:+UseParallelOldGC参数启用并行老年代垃圾回收器,以加速垃圾回收过程。
    • 可以通过设置-XX:+UseConcMarkSweepGC参数启用并发标记-清除垃圾回收器,以减少垃圾回收对应用程序的停顿时间。
  5. 调整线程数和线程栈大小:

    • 线程数和线程栈大小的设置也会影响应用程序的性能和稳定性。
    • 可以通过设置-Xss参数调整线程栈的大小,适当增加线程栈的大小可以防止栈溢出错误。
    • 可以通过设置-XX:ParallelGCThreads参数调整垃圾回收线程的数量,以充分利用多核系统的处理能力。

这些是一些常见的JVM调优策略,具体的调优方法和参数设置需要根据应用程序的特点和需求来确定。在进行调优时,建议进行性能测试和监控,同时注意观察内存使用情况、垃圾回收情况以及应用程序的响应时间等指标,以便及时调整和优化。

相关推荐
岁忧2 分钟前
(LeetCode 每日一题) 1865. 找出和为指定值的下标对 (哈希表)
java·c++·算法·leetcode·go·散列表
YuTaoShao5 分钟前
【LeetCode 热题 100】240. 搜索二维矩阵 II——排除法
java·算法·leetcode
1024小神11 分钟前
hono框架绑定cloudflare的d1数据库操作步骤
数据库
考虑考虑1 小时前
JDK9中的dropWhile
java·后端·java ee
想躺平的咸鱼干1 小时前
Volatile解决指令重排和单例模式
java·开发语言·单例模式·线程·并发编程
hqxstudying2 小时前
java依赖注入方法
java·spring·log4j·ioc·依赖
·云扬·2 小时前
【Java源码阅读系列37】深度解读Java BufferedReader 源码
java·开发语言
KellenKellenHao2 小时前
MySQL数据库主从复制
数据库·mysql
@ chen2 小时前
Redis事务机制
数据库·redis
KaiwuDB2 小时前
使用Docker实现KWDB数据库的快速部署与配置
数据库·docker