同步链表 - peek
synchronized LinkedList - peek
一个LinkedList
有方便的peek
、pop
、...方法。
不幸的是,我需要一个thread-safeLinkedList
。所以,我的第一个想法是将其包装如下:
List<Object> list = Collections.synchronizedList(new LinkedList<>());
但是,由于 List
接口不包含 peek
或 pop
方法。这当然行不通。
或者,我可以在整个代码中使用 synchronized(list)
块。这是要走的路吗?
我忽略了任何解决方案吗?
编辑:
使用 LinkedList
的原因有很多。我看到有人在提议其他 collections。所以,这里遵循简短的要求,这导致我决定使用 LinkedList
.
更多背景信息:
- 我正在使用 LinkedList,因为需要对项目进行排序。
- 应以 non-blocking 方式添加项目。
- 项目在后面添加;从前面移除。
- 在删除第一项之前,首先需要对其进行
peek
编辑和验证。如果验证失败,则该项目需要保留在列表中。
- 仅当验证成功完成时,才会删除第一项。
- 队列需要有一个最大大小(以避免内存问题)。
如果您需要 peek
工作,制作一个同步包装器可能不够,所以您必须明确地写 synchronized
。
与其说是编写包装器的问题,不如说是 peek
方法的语义问题。与表示单个动作的 pop
方法不同,peek
方法通常用于由 peek
组成的多组件动作,然后根据 [=12] 执行其他操作=] returns.
如果在包装器中同步,结果将与手动编写此代码相同:
String s;
synchronized(list) {
s = list.peek();
}
// <<== Problem ==>>
if (s != null) {
synchronized(list) {
s = list.pop();
}
}
这会带来一个问题,因为有时您的列表可能会在 peek
和 pop
之间变化(上面代码中的这个位置被标记为 "Problem" ).
进行检查和修改的正确方法是在单个 synchronized
块中进行,即
synchronized(list) {
String s = list.peek();
if (s != null) {
s = list.pop();
}
}
但是,这不能在简单的包装器中完成,因为两个列表操作在单个 synchronized
块中执行。
您可以通过构建自己的封装 LinkedList<T>
的数据结构来避免在多个地方写入 synchronized
,并提供在同步块中执行所有测试和修改操作的操作。但是,这不是一个简单的问题,因此您最好以一种可以与预定义的并发容器之一一起工作的方式更改算法。
你要的是并发Queue
。 LinkedList
实现了 List
、Deque
和 Queue
。 Queue
赋予它 FIFO(在后面添加,从前面删除)语义,带有 peek 和 pop。
A LinkedBlockingQueue 可以有界,这是你的标准之一。还有其他几个并发队列和双端队列可供选择。
一个LinkedList
有方便的peek
、pop
、...方法。
不幸的是,我需要一个thread-safeLinkedList
。所以,我的第一个想法是将其包装如下:
List<Object> list = Collections.synchronizedList(new LinkedList<>());
但是,由于 List
接口不包含 peek
或 pop
方法。这当然行不通。
或者,我可以在整个代码中使用 synchronized(list)
块。这是要走的路吗?
我忽略了任何解决方案吗?
编辑:
使用 LinkedList
的原因有很多。我看到有人在提议其他 collections。所以,这里遵循简短的要求,这导致我决定使用 LinkedList
.
更多背景信息:
- 我正在使用 LinkedList,因为需要对项目进行排序。
- 应以 non-blocking 方式添加项目。
- 项目在后面添加;从前面移除。
- 在删除第一项之前,首先需要对其进行
peek
编辑和验证。如果验证失败,则该项目需要保留在列表中。 - 仅当验证成功完成时,才会删除第一项。
- 队列需要有一个最大大小(以避免内存问题)。
如果您需要 peek
工作,制作一个同步包装器可能不够,所以您必须明确地写 synchronized
。
与其说是编写包装器的问题,不如说是 peek
方法的语义问题。与表示单个动作的 pop
方法不同,peek
方法通常用于由 peek
组成的多组件动作,然后根据 [=12] 执行其他操作=] returns.
如果在包装器中同步,结果将与手动编写此代码相同:
String s;
synchronized(list) {
s = list.peek();
}
// <<== Problem ==>>
if (s != null) {
synchronized(list) {
s = list.pop();
}
}
这会带来一个问题,因为有时您的列表可能会在 peek
和 pop
之间变化(上面代码中的这个位置被标记为 "Problem" ).
进行检查和修改的正确方法是在单个 synchronized
块中进行,即
synchronized(list) {
String s = list.peek();
if (s != null) {
s = list.pop();
}
}
但是,这不能在简单的包装器中完成,因为两个列表操作在单个 synchronized
块中执行。
您可以通过构建自己的封装 LinkedList<T>
的数据结构来避免在多个地方写入 synchronized
,并提供在同步块中执行所有测试和修改操作的操作。但是,这不是一个简单的问题,因此您最好以一种可以与预定义的并发容器之一一起工作的方式更改算法。
你要的是并发Queue
。 LinkedList
实现了 List
、Deque
和 Queue
。 Queue
赋予它 FIFO(在后面添加,从前面删除)语义,带有 peek 和 pop。
A LinkedBlockingQueue 可以有界,这是你的标准之一。还有其他几个并发队列和双端队列可供选择。