在我的应用中,我从socket.io监听器(线程1)
收到一些积分每次我这样做,我都将它们添加到Queue中,并且我有一个处理程序(另一个线程)每隔x毫秒运行一次以删除第一个值,并将其传递给UI线程。
Queue<Integer> fifo = new LinkedList<>();
// socket.io background thread (THREAD 1)
new run() {
Integer[] points = myapplication logic;
for (int point : points)
fifo.add (point);
}
// handler background thread (THREAD 2) runs every few milliseconds
new run {
Integer fiPoint = fifo.remove();
// THREAD 3 (MAIN UI THREAD)
runOnUIThread() {
do something with that fipoint;
}
}
我是否需要使用同步队列,或者我当前的实现是否正常?
答案 0 :(得分:1)
java.util.Stack
中的每个方法都已synchronized
,因此非常安全。但是你正在使用Queue
,这是灾难性的。
如果读者线程可以看到对象在队列中但看不到对象的内部,会发生什么?这会造成灾难。
考虑:
答案 1 :(得分:1)
对于队列,这不安全 - 您可以获得ConcurrentModificationException
0. fifo size(2)
1. Thread1 Calls fifo.push();
2. UI thread starts notify operation loping through 3 items
3. Thread2 Calls fifo.pop();
4. UI threads notify loop calls get(2) ... it's out of bounds
我的建议:
Queue<Integer> fifo = new ArrayBlockingQueue();
您可以阅读here
答案 2 :(得分:1)
为什么在有线程安全选项可用时,你甚至会考虑使用多线程的非线程安全类?你关注性能吗?不要。那是不成熟的优化。除非你实际测量了你的表现,并发现它缺乏,否则不要考虑使用Queue。
如果Queue不是线程安全的,那么你不能在多个线程中使用它。
为什么?
有两个问题。 原子性和可见性。
将整数存储在队列中并从队列中删除不保证是原子操作。例如,将项目添加到队列的后面,将涉及至少2个步骤。使用数组中的对象数增加计数器,然后将表示指向新Integer的指针的字节复制到某种存储容器中。一般来说,你不会知道发生了什么样的顺序而且你不在乎。但是,如果你增加计数,然后你可以复制其他线程接管并从队列中读取的引用,会发生什么。伯爵会说那里有东西,但它还没有被初始化。
其次,考虑可见性。当一个线程修改该计数器时,无法保证更改将可见另一个线程,更不用说及时了。这意味着一个线程可以递增变量,另一个线程可以将其递增到完全相同的值。这可能会使您的程序和队列本身永久损坏。
当然,你的Queue实现的编写者可以解决所有这些问题。如果他们这样做,他们会告诉你它是线程安全的。有时他们会明确表示它不是线程安全的(例如HashMap)。
当我不知道我的FIFO缓冲区需要多大时,我使用了LinkingBlockingDeque。当我知道缓冲区不会超过一定长度时,我已经使用了ArrayBlockingQueue。我从来没有测试过那个人的表现。