我们是否需要同步,如果我们通过一个线程添加并通过其他线程删除?

时间:2017-03-03 03:47:37

标签: java android multithreading thread-safety

在我的应用中,我从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;
   }
}

我是否需要使用同步队列,或者我当前的实现是否正常?

3 个答案:

答案 0 :(得分:1)

java.util.Stack中的每个方法都已synchronized,因此非常安全。但是你正在使用Queue,这是灾难性的。

如果读者线程可以看到对象在队列中但看不到对象的内部,会发生什么?这会造成灾难。

考虑:

  1. Writer线程标记队列中有一个对象。
  2. Reader线程看到队列中有一个对象。
  3. 读者线程访问对象的成员数据。
  4. Writer线程写入对象的成员数据。
  5. 哎呀,读者刚看到错误的数据。您需要确保对对象的所有写入都在&#34;之前发生。与读者线程的关系被告知对象在队列中。您的代码不包含任何内容。

答案 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。我从来没有测试过那个人的表现。