面试题答案
一键面试场景描述
假设有一个Kotlin类,其中包含一个可变的属性count
,多个线程同时对这个属性进行自增操作。
class Counter {
var count = 0
}
如果多个线程同时执行counter.count++
,由于自增操作并非原子操作,实际执行过程分为读取、增加、写入三个步骤,可能会出现一个线程读取了count
的值,还没来得及写入新值,另一个线程也读取了相同的值,导致最终结果比预期值小,出现数据不一致的问题。
解决方案及优缺点分析
- 使用
@Synchronized
关键字- 实现方式:
class Counter {
@Synchronized
var count = 0
}
- **优点**:实现简单,通过在属性上添加`@Synchronized`注解,Kotlin会在访问该属性的getter和setter方法上添加同步锁,确保同一时间只有一个线程能访问属性,从而保证线程安全。
- **缺点**:性能开销较大,因为所有线程访问该属性都需要竞争同一把锁,在高并发场景下,会降低系统的并发性能。
2. 使用AtomicInteger
- 实现方式:
import java.util.concurrent.atomic.AtomicInteger
class Counter {
private val atomicCount = AtomicInteger(0)
val count: Int
get() = atomicCount.get()
fun increment() {
atomicCount.incrementAndGet()
}
}
- **优点**:性能较好,`AtomicInteger`提供了原子性的操作方法,如`incrementAndGet`,这些方法通过硬件级别的CAS(Compare - And - Swap)操作实现,避免了传统锁的高开销,在高并发场景下表现更好。
- **缺点**:需要手动处理属性的包装和操作,代码相对复杂一些,并且与普通的`Int`类型属性相比,使用上略有不同。
3. 使用ConcurrentHashMap
来管理属性
- 实现方式:
import java.util.concurrent.ConcurrentHashMap
class Counter {
private val map = ConcurrentHashMap<String, Int>()
val count: Int
get() = map["count"]?: 0
fun increment() {
map.put("count", (map["count"]?: 0) + 1)
}
}
- **优点**:适用于需要将多个属性以键值对形式进行管理,并且需要线程安全的场景。`ConcurrentHashMap`提供了线程安全的操作,允许多个线程同时读取,部分线程写入,具有较高的并发性能。
- **缺点**:增加了内存开销,因为需要使用一个`ConcurrentHashMap`来存储属性,而且获取和修改属性的操作相对复杂,不适用于简单的单个属性场景。