1.Dubbo 是什么?是否了解过它的架构设计?
Dubbo官网:Apache Dubbo
Dubbo是一个高性能、轻量级的开源Java RPC框架,它提供了完整的RPC协议栈,包括服务发布、服务引用、负载均衡、容错、服务治理和服务监控等功能,同时提供了可扩展的RPC协议、数据模型、序列化和网络传输等组件,支持多语言和多协议。
Dubbo的架构设计主要包括服务提供者、服务消费者、注册中心和监控中心四个角色。其中,
服务提供者
提供服务的实现,并通过注册中心将自己注册到服务治理中心;服务消费者
则通过注册中心发现可用的服务,并通过负载均衡策略选择一个服务提供者进行调用;注册中心
主要负责服务的注册、发现和路由;监控中心
则负责服务的统计和监控。Dubbo的架构设计采用了分层架构模式,将不同的功能模块进行分离,以达到模块化和可扩展的目的。同时,Dubbo还提供了丰富的扩展点和插件机制,用户可以通过自定义扩展点和插件来满足不同的业务需求。
2.synchronized 关键字是什么,有什么作用?
说一说自己对于 synchronized 关键字的了解
synchronized关键字解决的是多个线程之间访问资源的同步性,synchronized关键字可以保证被它修饰的方法或者代码块在任意时刻只能有一个线程执行
在 Java 早期版本中, synchronized属于重量级锁,效率低下,因为锁监视器(monitor)是依赖于底层的操作系统来实现的。如果要挂起或者唤醒一个线程,都需要操作系统帮忙完成,而操作系统实现线程之间的切换时需要从用户态转换到内核态,这个状态之间的转换需要相对比较长的时间,这也是为什么早期的synchronized 效率低的原因.
Java 官方对从 JVM 层面对synchronized 较大优化,如自旋锁、适应性自旋锁、锁消除、锁粗化、偏向锁、轻量级锁等技术来减少锁操作的开销。 所以现在的 synchronized 锁效率也优化得很不错了。
说说自己是怎么使用 synchronized 关键字
修饰非静态方法, 锁的是this(当前对象), 也就是一个对象用一把锁
修饰静态方法, 锁的是 类.class , 也就是类的所有对象公用一把锁
修饰代码块.
尽量不要使用 synchronized(String a) 因为JVM中,字 符串常量池具有缓存功能
synchronized原理
synchronized
同步语句块的实现使用的是monitorenter
和monitorexit
指令,其中monitorenter
指令指向同步代码块的开始位置,monitorexit
指令则指明同步代码块的结束位置
当多个线程同时访问该方法,那么这些线程会先被放进对象的锁池,此时线程处于blocking状态
当一个线程获取到了实例对象的监视器(monitor)锁,那么就可以进入running状态,执行方法,此时
lock record
中的owner
设置为当前线程,count
加1表示当前对象锁被一个线程获取当running状态的线程调用wait()方法,那么当前线程释放monitor对象,进入waiting状态,
lock record
中的owner
变为null,count
减1,同时线程进入等待池,直到有线程调用notify()方法唤醒该线程,则该线程重新获取monitor对象进入owner
如果当前线程执行完毕,那么也释放monitor对象,进入waiting状态,
lock record
中的owner
变为null,count
减1JDK1.6 之后的 synchronized 底层做了哪些优化?
java的线程模型是1对1的, 加锁需要调用操作系统的底层原语mutex, 所以每次切换线程都需要操作系统切换到内核态, 开销很大. 这也是之前synchronized的问题所在, jdk1.6对其进行了优化, 从无锁到偏向锁到轻量级锁到重量级锁 自旋锁就不需要阻塞, 也就不需要操作系统切换为内核态去做, 所以短时间的自旋开销是比较低的.
JDK 1.6 引入了偏向锁和轻量级锁,从而让锁拥有了四个状态:无锁状态(unlocked)、偏向锁状态(biasble)、轻量级锁状态(lightweight locked)和重量级锁状态(inflated)。 虚拟机对象头里锁标志位, 就记录了这4中状态.
偏向锁
大多数时候是不存在锁竞争的,常常是一个线程多次获得同一个锁,因此如果每次都要竞争锁会增大很多没有必要付出的代价,为了降低获取锁的代价,才引入的偏向锁。
当锁对象第一次被线程获得的时候,使用 CAS 操作将线程 ID 记录到对象头的MarkWord中,这个线程以后每次进入这个锁相关的同步块就不需要再进行任何同步操作。
当有另外一个线程去尝试获取这个锁对象时,偏向状态就宣告结束,此时撤销偏向后恢复到未锁定状态或者轻量级锁状态。
轻量级锁
轻量级锁考虑的是竞争锁对象的线程不多,而且线程持有锁的时间也不长的情景。因为阻塞线程需要CPU从用户态转到内核态,代价较大,如果刚刚阻塞不久这个锁就被释放了,那这个代价就有点得不偿失了,因此这个时候就干脆不阻塞这个线程,让它自旋这等待锁释放。
轻量级锁是相对于传统的重量级锁而言,它使用自旋 + CAS 操作来避免重量级锁使用互斥量的开销。
长时间的自旋会使CPU一直空转, 浪费CPU, 所以这里自旋是适应性自旋, 自旋时间由上一个线程自旋的时间决定的.
线程自旋的次数到了阈值, 另外一个线程还没释放锁, 那么就膨胀为重量级锁。
如果有两条以上的线程争用同一个锁,那轻量级锁就不再有效,要膨胀为重量级锁。
锁消除
锁消除是指对于被检测出不可能存在竞争的共享数据的锁进行消除。
锁消除主要是通过逃逸分析来支持,如果堆上的共享数据不可能逃逸出去被其它线程访问到,那么就可以把它们当成私有数据对待,也就可以将它们的锁进行消除。
锁粗化
如果一系列的连续操作都对同一个对象反复加锁和解锁,频繁的加锁操作就会导致性能损耗。
比如连续使用StringBuffer的append() 方法就属于这类情况。如果虚拟机探测到由这样的一串零碎的操作都对同一个对象加锁,将会把加锁的范围扩展(粗化)到整个操作序列的外部, 这样只需要加锁一次就可以了。
3.如何设计一个点赞系统?
设计一个点赞系统可以分为以下几个步骤:
确定需求:需要明确点赞的对象是什么,是否需要计数等信息,同时需要考虑点赞的业务场景,如用户点赞、文章点赞等。
数据库设计:需要设计点赞相关的数据表,可以包含点赞者 ID、被点赞对象 ID、点赞时间等字段。
接口设计:需要设计点赞相关的接口,包括点赞、取消点赞、查询点赞数等操作。
业务逻辑实现:在接口中实现点赞相关的业务逻辑,包括判断点赞状态、更新点赞数、更新点赞状态等操作。
安全性考虑:需要考虑并发访问的情况,可以使用分布式锁来保证数据一致性和安全性。
性能优化:如果点赞系统的访问量很高,可以使用缓存来提高性能,比如使用 Redis 来缓存点赞数等信息。
监控和日志:需要对点赞系统进行监控和日志记录,以便及时发现和排查问题。
总之,设计一个点赞系统需要综合考虑需求、数据库设计、接口设计、业务逻辑实现、安全性、性能优化等方面,同时需要不断优化和完善。