怎么保证线程安全?
线程安全问题是指在多线程背景下,线程没有按照我们的预期执行,导致操作共享变量出现异常。
在Java中有许多同步方案提供给我们使用,从轻到重有三种方式:原子类、volatile关键字、锁。
原子类是juc atomic包下的一系列类,通过CAS比较与交换的机制实现线程安全的更新共享变量。通过预期值与内存值的比较来判断是否修改。
volatile关键字是轻量级的同步机制,他实现了变量的可见性、防止指令重排序。保证了【单个变量】读写的线程安全。可见性问题是JMM内存模型中定义每个核心存在一个内存副本导致的,核心只操作他们的内存副本,volatile保证了一旦修改变量则立即刷新到共享内存中,且其他核心的内存副本失效,需要重新读取。
原子类和volatile只能保证单个共享变量的线程安全,锁则可以保证临界区内的多个共享变量线程安全。
java中常用的锁有两种:synchronized+juc包下的lock锁。synchronized锁是互斥锁,可以作用于实例方法、静态方法、代码块,基于对象头和Monitor对象,在1.6之后引入轻量级锁、偏向锁等优化。lock锁接口可以通过lock、unlock方法锁住一段代码,基于AQS实现,其加锁解锁就是操作AQS的state变量,并且将阻塞队列存在AQS的双向队列中。除了锁以外,juc包下还提供了一些线程同步工具类,如CountDownLatch、Semaphore等等,我们还可以使用ThreadLocal定义线程局部变量!
答案参考来源
Redis的持久化策略。
1.RDB: redis database 在指定的时间间隔内,将内存中的数据集的快照写入磁盘,文件名dump.rdb 适合大规模的数据恢复,对数据库的完整性和一致性要求不是很高 一定时间间隔备份一次,如果数据库意外down掉,就会失去最后一次快照的所有修改
2.AOF: append only file 以日志的形式记录每个写操作,只允许追加文件,不允许改写文件,redis启动时会读取这个文件,并从头到尾执行一遍,以此来恢复数据,文件名appendonly.aof 在最恶劣的环境下,也丢失不会超过2秒的数据,完整性较高,但是会对磁盘持续的进行IO,代价太大。企业级最少需要5G才能支持 如果.aof文件大小超过原来的一倍,会进行重写压缩,保留最小的指令集合
3.优先级 aof>rdb
答案参考来源
说说分布式锁,以及如何应用。
SpringBoot常用的注解。
- 启动注解 @SpringBootApplication (是一个复合注解,包含了@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan这三个注解)
- Controller 相关注解 @Conroller @RestController @RequestMapping @RequestBody
- 取请求参数值 @PathVariable @RequestParam @CookieValue @RequestHeader
- 注入bean相关 @Repository @Service @Scope @Entity @Component @Bean Autowired
- 导入配置文件 PropertySource @Value @Import @ImportResource
- 事务注解 @Transactional
- 全局异常处理 @ControllerAdvice @ExceptionHandler
- 验证条件注解 @ConditionalOnProperty
Select、from、where、group by、having优先级。
语法顺序:select->from->where->group by->having->order by -> limit
执行顺序:from --> where – > group by --> having --> select --> order by --> limit
数据库的三大范式。
-
目前关系数据库有六种范式:第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、巴斯-科德范式(BCNF)、第四范式(4NF)和第五范式(5NF,又称完美范式)。
-
数据库中常用的“三大范式”,分别为:第一范式(1NF)、第二范式(2NF)、第三范式(3NF)。
-
第一范式(1NF):列的原子性,不能再分割。
-
第二范式(2NF):确保数据库表中的每一列都和主键相关。
-
第三范式(3NF):在2NF基础上,任何非主属性不依赖于其它非主属性(在2NF基础上消除传递依赖);第三范式需要确保数据表中的每一列数据都和主键直接相关,而不能间接相关。
参考
笔试题:java自动装箱和自动拆箱。
- 自动装箱:自动将基本类型转化成与之对应的包装类,其实是系统在编译时自动调用了包装类的valueOf()方法,但需要注意的是装箱时涉及缓存问题,如果该值在缓存范围内就在缓存中取,不在的话就新建一个对象,所以和直接new包装类的对象是不同的。我们用基础类型给包装类进行赋值或传参的时候会自动装箱。
- 自动拆箱:自动将包装类里对应的基本类型的值返回出来,其实是在编译时自动调用了包装类的xxxValue()方法。我们用包装类给基础类进行赋值、传参或他们之间进行比较(<,>,<=,>=,==)或运算的时候会自动拆箱。
笔试题:Java19的新特性
笔试题:sleep()与wait()的区别。
- 使用 sleep 方法可以让线程休眠,而使用 wait 方法则必须放在 synchronized 块里面。wait还需要额外的方法notify/ notifyAll 进行唤醒,它们同样需要放在 synchronized 块里面,且获取对象的锁。当然也可以使用带时间的 wait(long millis) 方法,时间一到,无需其他线程唤醒,也会重新竞争获取对象的锁继续执行。sleep方法短暂休眠之后会主动退出阻塞,而没有指定时间的wait方法则需要被其他线程中断后才能退出阻塞。sleep()和yield()是Thread类的方法。wait() 是Object中定义的native方法。sleep()方法自带sleep时间,时间过后,Thread会自动被唤醒。 或者可以通过调用interrupt()方法来中断。