java 线程安全链表_java – 线程安全的排序链表

博主通过实验对比了两种线程安全的已排序单链表实现:细粒度同步和粗粒度同步。在4个线程并发插入20000个整数的场景下,发现粗粒度同步的性能优于细粒度同步。细粒度同步的总CPU时间为1620ms,而粗粒度同步为1298ms。尽管细粒度同步的.lock()和.unlock()操作仅占30%的时间,但其缓存未命中可能成为性能瓶颈。博主对结果感到困惑,寻求原因。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我正在尝试编写一个线程安全的已排序单链表.我写了两个版本:粗粒度同步和细粒度同步.以下是两种实现:

细粒度:

public void add(T t) {

Node curr = head;

curr.lock.lock();

while (curr.next != null) {

// Invariant: curr is locked

// Invariant: curr.data < t

curr.next.lock.lock();

if (t.compareTo(curr.next.data) <= 0) {

break;

}

Node tmp = curr.next;

curr.lock.unlock();

curr = tmp;

}

// curr is acquired

curr.next = new Node(curr.next, t);

if (curr.next.next != null) { // old curr's next is acquired

curr.next.next.lock.unlock();

}

curr.lock.unlock();

}

粗粒度:

public void add(T t) {

lock.lock();

Node curr = head;

while (curr.next != null) {

if (t.compareTo(curr.next.data) <= 0) {

break;

}

curr = curr.next;

}

curr.next = new Node(curr.next, t);

lock.unlock();

}

我将4个线程(在4个逻辑CPU核心上)的两个版本定时插入20000个整数.每个线程的时间显示CPU时间(即它不包括等待时间).

Fine grained:

Worked 1 spent 1080 ms

Worked 2 spent 1230 ms

Worked 0 spent 1250 ms

Worked 3 spent 1260 ms

wall time: 1620 ms

Coarse grained:

Worked 1 spent 190 ms

Worked 2 spent 270 ms

Worked 3 spent 410 ms

Worked 0 spent 280 ms

wall time: 1298 ms

我最初的想法是.lock()和.unlock()是问题,但我分析了实现,他们一起只消耗了30%的时间.我的第二个猜测是,细粒度的解决方案有更多的缓存未命中,但我怀疑它,因为单个链表与数组不同,本质上容易出现缓存未命中.

知道为什么我没有得到预期的并行化吗?

Java中有一个线程安全链表实现,即`CopyOnWriteArrayList`。它是`List`接口的一个实现类,提供了线程安全的操作。 `CopyOnWriteArrayList`的特点是在进行写操作时,会创建一个新的副本来进行操作,而原始数据不会被修改。这样可以保证读操作的并发性,因为读操作可以同时进行,而不需要加锁。只有在写操作时会进行加锁,确保数据一致性。 由于每次写操作都需要复制整个链表,所以在频繁进行写操作时,性能会受到影响。因此,`CopyOnWriteArrayList`适用于读操作远远多于写操作的场景。 以下是一个使用`CopyOnWriteArrayList`的示例代码: ```java import java.util.concurrent.CopyOnWriteArrayList; public class ThreadSafeLinkedList { private CopyOnWriteArrayList<Integer> list; public ThreadSafeLinkedList() { list = new CopyOnWriteArrayList<>(); } public void addElement(int element) { list.add(element); } public void removeElement(int element) { list.remove(Integer.valueOf(element)); } public int getSize() { return list.size(); } } ``` 在上面的示例中,我们使用`CopyOnWriteArrayList`来实现一个线程安全链表。通过`addElement`方法向链表中添加元素,通过`removeElement`方法从链表中移除元素,通过`getSize`方法获取链表的大小。 需要注意的是,虽然`CopyOnWriteArrayList`提供了线程安全的操作,但并不意味着我们可以在所有情况下都使用它。在特定的业务场景下,需要根据实际需求选择适合的线程安全容器。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值