0

我必须创建一个在 Java 中使用线程和管道的程序。我必须创建 2 个线程类。第一课产生一个随机数,然后它必须传递给第二课(它必须增加一次),然后它必须回到第一课,这将被打印出来。要传输这个整数,我必须使用 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 来解决我的问题。我不能使用其他任何东西。

4

2 回答 2

1

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

需要了解的基本知识是:

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

您将需要第一个让两个线程访问同一条数据而不会互相踩到对方的脚趾,第二个让每个线程知道另一个线程完成了它的工作部分。

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

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

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

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

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

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

信号量是非常通用的对象,可用于在线程之间发出事件信号,尽管它的接口不是很能说明问题:

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

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

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

编辑:

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

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

算法变为:

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

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

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

该解决方案有点麻烦,尽管它确实有助于理解序列化的概念。幸运的是,Java 让您可以非常轻松地处理序列化。有关详细信息和可能的陷阱,请参阅关于该主题的其他问题。由于它依赖于与上述流方法相同的机制,因此在尝试反序列化尚未序列化的值时,线程将以类似的方式阻塞。

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


(*) 我会泄漏 (java) bean:您实际上可以使用任何 java 对象作为信号机制,使用它们上的waitandnotify方法,但这对于教程来说会很有趣吗?

[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)

于 2013-05-03T19:18:56.530 回答
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允许将元素累积到固定容量,这可以提高运行时的性能,但也可能会浪费 CPU 时间和内存来生成未使用的元素。

于 2013-05-03T18:31:24.907 回答