自定义继承自AbstractList的线程安全集合类
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
public class ThreadSafeList<E> extends AbstractList<E> {
private final List<E> list;
private final ReentrantLock lock = new ReentrantLock();
public ThreadSafeList() {
this.list = new ArrayList<>();
}
@Override
public E get(int index) {
lock.lock();
try {
return list.get(index);
} finally {
lock.unlock();
}
}
@Override
public int size() {
lock.lock();
try {
return list.size();
} finally {
lock.unlock();
}
}
@Override
public E set(int index, E element) {
lock.lock();
try {
return list.set(index, element);
} finally {
lock.unlock();
}
}
@Override
public void add(int index, E element) {
lock.lock();
try {
list.add(index, element);
} finally {
lock.unlock();
}
}
@Override
public E remove(int index) {
lock.lock();
try {
return list.remove(index);
} finally {
lock.unlock();
}
}
}
同步机制阐述
- 锁的使用策略:使用
ReentrantLock
,它是可重入的独占锁。在执行每个可能修改或读取集合状态的方法(增删改查)时,先获取锁,方法执行完毕后释放锁。这种策略简单直接,确保同一时间只有一个线程能够访问和修改集合,从而保证线程安全。
- 处理并发冲突:由于
ReentrantLock
是独占锁,当一个线程获取锁后,其他线程尝试获取锁时会被阻塞,直到持有锁的线程释放锁。这就避免了多个线程同时修改集合导致的数据不一致问题。
- 保证数据的一致性和完整性:通过在关键操作前后加锁,确保在对集合进行修改操作(如添加、删除、修改元素)时,其他线程无法同时进行干扰。读取操作也加锁,保证读取到的数据是完整且一致的,不会出现读取到部分修改数据的情况。
与Java Collections框架中现有线程安全集合类同步机制的比较
- 优势:
- 灵活性:自定义的同步机制相对简单直观,开发者可以根据具体需求灵活调整锁的使用范围和策略。例如,可以在特定方法中根据业务逻辑选择性地获取锁,而不是像某些现有集合类那样对整个集合的操作都采用统一的同步方式。
- 理解成本低:对于小型项目或开发者对代码可读性要求较高的场景,这种简单的基于
ReentrantLock
的实现更容易理解和维护,相比一些复杂的Java Collections框架中的同步机制,新人上手更快。
- 劣势:
- 性能开销:与一些更优化的现有线程安全集合类(如
CopyOnWriteArrayList
)相比,在高并发读多写少的场景下,ReentrantLock
的频繁加锁和解锁操作会带来较大的性能开销。CopyOnWriteArrayList
在读取时不需要加锁,因为它是基于数组的复制机制,读操作不会影响写操作,从而提高了并发读的性能。
- 功能完整性:Java Collections框架中的线程安全集合类通常经过了大量的测试和优化,并且提供了丰富的功能和特性。例如
ConcurrentHashMap
提供了高效的并发哈希表实现,支持分段锁等优化机制,相比之下,自定义的集合类功能相对单一,可能无法满足复杂的业务需求。