简体   繁体   English

如何将一个just对象实例传递给java中的一个线程,这样即使实例值得到更新也不应该影响使用它的线程

[英]How can I pass a just object instance to a thread in java, so that even if the instance value gets updated should not effect the thread using it

I am developing an application in which I continuously receive messages. 我正在开发一个我不断收到消息的应用程序。 I am storing these messages in an in-memory data structure (say List). 我将这些消息存储在内存数据结构中(比如List)。 I want to write those messages to a file but only after list size reaches some threshold value say 100 messages (perform batch processing on the messages). 我想将这些消息写入文件,但只有在列表大小达到某个阈值后才说100条消息(对消息执行批处理)。 One way is I can simply check the list size after every message is received and call a function to write messages to a file if Threshold value is reached. 一种方法是我可以在收到每条消息后简单地检查列表大小,如果达到阈值,则调用函数将消息写入文件。 But the problem with this approach is: 但这种方法的问题是:

  1. Calling function may need to wait indefinitely until all the messages are written to the file 调用函数可能需要无限期地等待,直到所有消息都写入文件
  2. The incoming messages may be lost in the process or might need to wait for getting stored in the List. 传入的消息可能在此过程中丢失,或者可能需要等待存储在列表中。

Other way could be to spawn a new thread, which will write messages to a file independently. 其他方法可能是生成一个新线程,它将独立地将消息写入文件。 But when I pass the list (containing messages) to the thread for performing write operation, it gets updated with new messages which are continuously coming in. As a result newly arrived messages also gets written to the file which is not expected. 但是当我将列表(包含消息)传递给线程以执行写操作时,它会被新消息更新,这些消息会不断进入。因此,新到达的消息也会被写入到不期望的文件中。

This should not happen as I am intending the new messages to be written in the next batch. 这不应该发生,因为我打算在下一批中写入新消息。

Can someone suggest me a solution for this requirement, or any improvements in the above approach that can solve my issues. 有人可以建议我这个要求的解决方案,或上述方法的任何改进,可以解决我的问题。

I find a cleaner solution is to support auto-batching. 我发现更清洁的解决方案是支持自动批处理。 ie where the size of the batch adjusts with the rate of incoming data. 即批量大小随输入数据的速率调整。

To do this you can use a BlockingQueue 为此,您可以使用BlockingQueue

// unbound queue will not block the producer.
final BlockingQueue<T> queue = new LinkedBlockingQueue<T>();

// to add an element.
queue.add(element);

// to get a batch of data
List<T> list = new ArrayList<T>(maxElements);
while(writing) {
    T t = queue.take(); // wait for at least one element.
    list.add(t);
    queue.drainTo(list, maxElements-1);
    // process list, e.g. write to a file.
    list.clear();
}

The benefit of this approach is that if the producer is very slow, you won't get elements held unreasonably long, but as the rate increases the batch size naturally grows to whatever will keep up with the producer which means you don't have to decide what is the best batch size to use. 这种方法的好处是,如果生产者非常慢,你不会让元素保持不合理的长,但随着速度的增加,批量大小自然会增长到跟上生产者的任何东西,这意味着你不需要决定使用的最佳批量大小。

I would suggest the following approach: 我建议采用以下方法:

  1. Hold a reference to the message list in an AtomicReference . 保持对AtomicReference的消息列表的引用。
  2. When the list is full enough, replace it with a new, empty list; 当列表足够满时,将其替换为新的空列表;
  3. pass the full list to a worker thread that will save the messages to a file. 将完整列表传递给工作线程,该工作线程将消息保存到文件中。

If you write to the list from a single thread, it will be enough to use a plain reference instead of the AtomicReference . 如果从单个线程写入列表,则使用普通引用而不是AtomicReference就足够了。

It's important to understand that you never pass an object around in Java - only ever a reference (or a primitive value). 重要的是要理解你永远不会在Java中传递一个对象 - 只有一个引用 (或一个原始值)。

Options: 选项:

  • Create a copy of the list, and pass a reference to that copy to your new thread 创建列表的副本,并将对该副本的引用传递给新线程
  • Use a producer/consumer queue, so your "producing" thread only ever adds values to the queue, and your consumer thread only ever takes items from the queue to write them to disk. 使用生产者/消费者队列,那么你的“生产”线程永远只能增加值到队列中,和你的消费者线程永远只需要从队列中的项目并写入到磁盘中。 You'll want to think about how large you want the queue to potentially get before it stops accepting more entries, of course. 当然,您需要考虑队列在停止接受更多条目之前可能获得的大小。

I'd recommend the latter approach, using the classes in the java.util.concurrent package to implement it; 我建议使用后一种方法,使用java.util.concurrent包中的类来实现它; particularly BlockingQueue<E> implementations. 特别是BlockingQueue<E>实现。

为什么主消息接收进程一旦将旧消息列表传递给文件写入线程就会创建新消息列表?

You can implement Custom BoundedQueue using Condition which accepts say 100 objects and then write at one go. 您可以使用Condition实现Custom BoundedQueue ,它接受100对象,然后一次写入。

Now you can share this BoundedQueue class instance with different threads which will put objects in it and there will be thread which will call writeAll() method until you want to call it. 现在你可以用不同的线程共享这个BoundedQueue类实例,它将把对象放在其中,并且会有一个调用writeAll()方法的线程,直到你想调用它为止。

BoundedBuffer boundedBuffer  = new BoundedBuffer();
boundedBuffer.put("test"); .......

From writing thread do below 从写作线程做下面

boundedBuffer.writeAll();

Below is the sample code 下面是示例代码

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class BoundedBuffer {
final Lock lock = new ReentrantLock();
final Condition full = lock.newCondition();
final Condition empty = lock.newCondition();

final Object[] items = new Object[100];
int count;

public void put(Object x) throws InterruptedException {
    lock.lock();
    try {
        while (count == items.length) {
            empty.signal();
            full.await();
        }
        items[count] = x;
        ++count;
    } finally {
        lock.unlock();
    }
}

public void writeAll() throws InterruptedException {
    lock.lock();
    try {
        while (count < items.length)
            empty.await();
        // Write to file here After write finished signal full condition
        count = 0;
        full.signal();

    } finally {
        lock.unlock();
    }
}
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM