java在线程之间共享数据

时间:2011-03-04 07:23:19

标签: java multithreading thread-safety client

我有一个从socket服务器读取数据的java进程。因此,我有一个BufferedReader和一个PrintWriter对象对应于该套接字。

现在在同一个java进程中,我有一个接受客户端连接的多线程java服务器。我希望实现一种功能,其中我接受的所有这些客户端都可以从我上面提到的BufferedReader对象中读取数据。(这样它们可以复用数据)

如何让这些单独的客户端线程从BuffereReader单个对象中读取数据?很抱歉这是混乱。

3 个答案:

答案 0 :(得分:6)

我强烈建议他们直接访问BufferedReader。假设你知道数据的格式,并且每个客户端都试图读取相同的格式(如果不是这样的话,我看不出它是如何工作的)我建议创建一个线程来读取{ {1}}并将工作项放在BufferedReader中。在Java中使用生产者/消费者队列的示例有 lot ,这也可能使测试客户端代码变得更容易。

只有一个线程访问Queue意味着你不必担心定义一个所有其他线程必须内容的原子操作 - 你的阅读线程通过决定添加一个工作来有效地定义该操作项目到队列。

编辑:如果所有客户都应该看到所有的数据,那么这强化了我建议更进一步使用单个阅读器 - 除了没有BufferedReader数据的项目是从中移除,您将拥有一个集合,客户可以从中读取所有现有数据。您需要使用适当的线程安全集合,但Java中有很多这样的集合。

编辑:刚刚阅读了您的评论,其中说每个客户应该只看到从阅读器读取的最后一项,这使得它更容易。让一个线程读取数据,但只保留一个变量,引用“最后读取的项目”。您可能要么同步对它的访问权限,要么使用Queue,但这两者都很容易。

答案 1 :(得分:1)

最简单的事情可能是创建一个临时文件并使用java.nio.channels.FileChannel。原因是这已经提供了你想要的读/写语义。另一种方法是使用内存方案自行重新实现这些语义。

您要做的是从您的阅读器中读取并附加到文件的末尾。然后,您的读者将获得最初指向文件末尾的文件通道。当你只是等待阅读器的更多输入时,你必须小心确保他们不认为他们已经到达了流的末尾。您可以让下游逻辑意识到这一点,或者为读者提供某种包装。

编辑:这是在了解您希望所有读者都能看到所有内容的情况下编写的。如果事实上并非你想要的那样,这太复杂了。

答案 2 :(得分:1)

如果您无法将所有数据保存在内存中,您可以使用以下内容: (使用经典的观察者模式,其中一个线程监视源,其他线程通知新数据)

class Broadcaster {
    private final ConcurrentSkipListSet<Listener> listeners =
         new ConcurrentSkipListSet<Listener>();

    private final BufferedReader source = //inject source

    // register / de-gegister code
    public void register(Listener listener);
    public void deRegister(Listener listener);
    // usually it's used like broadcaster.register(this);

    public void broadcast(){
        String read = null;
        while((read = source.readLine())!=null){
            for(Listener listener : listeners){
                listener.send(read);
            }
        }
    }
 }

class Listener implements Runnable {
    private final Queue<String> queue = new LinkedBlockingQueue<String>();

    public void send(String read){
        queue.add(read);
    }

    public void run(){
        while(!Thread.currentThread().isInterrupted()){
            String got = queue.get();
            System.out.println(got);
        }
    }
}

我没有测试这个东西或任何东西,所以如果它不起作用就怜悯!

编辑:如果你有内存限制,你可能也想这样做:

Queue<String> queue = new LinkedBlockingQueue<String>(2000);

这将对队列中可以排队的元素数量设置一个上限,当达到此限制时,想要在其上放置元素的线程必须等待(如果使用add,那么是)。这样,当侦听器无法足够快地消耗数据时,Broadcaster将阻塞(等待)(而在这个简单的方案中,其他侦听器可能会饿死)。

EDIT2:执行此操作时,最好只在队列中放置不可变的内容。如果你举例如byte[],不礼貌的听众可能会改变数据而其他线程可能会感到惊讶。如果这在某种程度上是不可能的,你应该在每个队列上放置一个不同的副本,例如byteArray.clone()。这可能会引起一些性能损失。