使用2个管道在线程之间传输

时间:2013-05-03 17:32:34

标签: java multithreading pipe

我必须创建一个在Java中使用线程和管道的程序。 我必须创建两个线程类。第一类产生一个随机数,然后它必须传递到第二类(它必须增加一次)然后它将不得不返回到将被打印出来的第一类。要传输此整数,我必须使用2个管道来传输此整数。

快捷方式:

Class 1:
Creates a random number.
Sends it to the class 2.

Class 2:
Increase the number by 1.
Send it back to the class 1.

Class 1:
Prints the result.

如何制作类似的东西? 谢谢你的帮助!

我必须使用类PipedOutputStream和PipedInputStream来解决我的问题。我不能使用任何其他东西。

2 个答案:

答案 0 :(得分:1)

看起来你想学习java多线程的原理。我通常建议使用并发队列,但也许你需要使用同步原语自己实现一些东西。

基本要知道的事情是:

  • 用于并发访问的线程之间的套利
  • 线程之间的信令以指示某些事件的变化或即将发生的事件

你需要第一个让两个线程访问相同的数据而不踩到彼此的脚趾,第二个让每个线程知道另一个线程完成了它的部分工作。

正如您在问题正文中指出的那样,算法大纲类似于:

  • thread(1)设置值
  • thread(1)告诉线程(2)它可以处理数据
  • thread(2)更新值
  • thread(2)告诉线程(1)它已经完成了数据的处理
  • thread(1)显示结果

如果正确完成,您实际上不需要处理对变量的并发访问,因为每个线程将单独执行其工作,并等待另一个线程的信号来访问数据。

更详细地说,整体执行情况如下:

  • thread(2)等待thread(1)信号
  • thread(1)设置值
  • thread(1)告诉线程(2)它可以处理数据
  • thread(1)等待thread(2)信号
  • thread(2)更新值
  • thread(2)告诉线程(1)它已经完成了数据的处理
  • thread(1)显示结果

现在,您可以将线程各自的指令序列分开,以了解每个线程需要自己做什么。

Semaphores是非常通用的对象,可用于在线程之间发出事件信号,尽管它的界面不是很有说服力:

  • 必须将实例初始化为0
  • acquire方法的调用在语义上与事件的 wait 相同
  • release的调用将对应于信号触发器

因此,每个线程必须包含对要操作的变量的引用,以及对同一信号量实例的引用,以便在(*)上进行同步。

你的程序的下一步可能是将它变成一个更通用的生产者消费者设置,其中第一和第二个线程处理多个值而不仅仅是一个值。这是您需要使用并发队列类的地方。也就是说,鉴于上述解释,您可能希望首先自己实现这样的队列。

编辑:

您的更新表明您希望使用PipedInputStream和PipedOutputStream作为线程之间的通信媒介。

在此设置中,管道允许您的线程自然同步,因为输入管道的read方法是阻塞的。您需要为每个线程提供一对输入和输出管道,其中第二个输入是connect到第一个输出的输出,反之亦然。

算法变为:

  • thread(2)从输入管道读取(并阻塞直到收到它)
  • thread(1)计算初始值
  • thread(1)write是其输出管道的值
  • thread(1)从其输入管道(和块)读取
  • thread(2)从对read
  • 的调用返回
  • thread(2)处理它收到的整数
  • thread(2)将新值写回其ouptput管道
  • thread(1)从其read
  • 返回
  • thread(1)打印结果

与前面的算法一样,每个指令序列可以在其自己的功能中单独实现。难点可能是将整数从其本机表示形式来回转换为可以作为字节流读取和写入的整数。此过程称为序列化和反序列化。

一种方法是使用Integer类工具转换为String(使用toStringparseInt方法调用),然后转换字符串本身在与流一起使用的字节数组中。

这个解决方案有点麻烦,虽然它确实有助于理解序列化的想法。幸运的是,Java让您可以轻松处理序列化。有关详细信息和可能的陷阱,请参阅此other question关于该主题的信息。由于它依赖于与前面提到的流方法相同的机制,因此当尝试反序列化尚未序列化的值时,线程将以类似的方式阻塞。

有关详细信息,请参阅管道类的connect,[read] [3]和[write] [4]方法。


(*)我将溢出(java)bean:您实际上可以使用任何java对象作为信号机制,使用waitnotify方法,但是教程很有趣吗?

[3]:http://docs.oracle.com/javase/6/docs/api/java/io/PipedInputStream.html#read(byte[],int,int) [4]:http://docs.oracle.com/javase/6/docs/api/java/io/PipedOutputStream.html#write(byte[],int,int)

答案 1 :(得分:0)

为什么不使用java.util.concurrent.BlockingQueue?你可以有这样的设置:

class WriteThread extends Thread {

    private BlockingQueue<Integer> createdIntegers;
    private BlockingQueue<Integer> processedIntegers;

    public WriteThread(BlockingQueue<? extends Integer> createdIntegers, BlockingQueue<? extends Integer> processedIntegers){
        this.createdIntegers = createdIntegers;
        this.processedIntegers = processedIntegers;
    }

    private boolean going = true;
    public void stopReadingAndWriting(){
        going = false;
        interrupt();
    }
    public void run(){
        while(going){
            try {
                createdIntegers.put(generateRandomInteger());
                System.out.println(processedIntegers.take());
            } catch (InterruptedException ie){
                // stop-in-the-middle handling code goes here
            }
        }
    }
}

class CalculationThread extends Thread {

    private BlockingQueue<Integer> createdIntegers;
    private BlockingQueue<Integer> processedIntegers;

    public CalculationThread(BlockingQueue<? extends Integer> createdIntegers, BlockingQueue<? extends Integer> processedIntegers){
        this.createdIntegers = createdIntegers;
        this.processedIntegers = processedIntegers;
    }

    private boolean going = true;
    public void stopReadingAndWriting(){
        going = false;
        interrupt();
    }

    public void run(){
        while(going){
            try {
                Integer integer = createdIntegers.take();
                integer = performCalculation(integer);
                processedIntegers.put(integer);
            } catch (InterruptedException ie){
                // stop-in-the-middle handling code goes here
            }
        }
    }
}

另外,我建议使用三个线程,一个用于生成整数,一个用于计算,一个用于写入。这样,写入或生成速度不会捆绑在一起。

请注意,您可以根据需要使用BlockingQueue的不同实现。 SynchronousQueue类将在队列中放置和取出之间进行一对一的对应,因此您不会存储大量未处理或未写入的整数,这对于内存有益,但如果由于某种原因,创建存储的线程挂起。其他实现(如LinkedBlockingQueue)允许将元素sup构建到固定容量,从而提高运行时的性能,但也可能浪费CPU时间和内存生成未使用的元素。