我正在一个项目中,我有一个类DeviceCommunicator
,即implements Runnable
。当前,主类实例化了DeviceCommunicator
的单个实例,该实例最终(最终)使用Socket
库连接到本地网络上的设备。
最终,我的意思是,如果需要发送消息,则DeviceCommunicator
的实例打开与设备的套接字连接,发送消息,然后启动一个新线程,以通过下面的代码行:new Thread(new DeviceCommunicator()).start();
编辑:澄清一下,这是程序执行时的操作顺序:
1)MAIN
类实例化带有构造函数的DeviceCommunicator
类,如下所示:comm1 = DeviceCommunicator(hostName, portNum)
2)MAIN
类想要向comm1
发送一条消息,因此它像下面这样调用send
:comm1.send(someString)
3)comm1的类型为DeviceCommunicator
,并打开到hostName/portNum的Socket
连接,如下所示:deviceSocket = new Socket(hostName, portNum);
out = new PrintStream(deviceSocket.getOutputStream());
in = new BufferedReader(new InputStreamReader(deviceSocket.getInputStream()));
4)comm1将someStr
发送到输出PrintStream
,然后使用以下代码初始化线程以监听响应:new Thread(new DeviceCommunicator()).start();
由于监听的DeviceCommunicator
线程没有构造函数参数,因此需要我将输出PrintStream
和输入BufferedReader
static
变量。
当我只有一个DeviceCommunicator
实例时-效果很好!
但是,我希望DeviceCommunicator
类的多个实例可以连接到本地网络上的同一设备或不同设备,但考虑到DeviceCommunicator
类的输出和输入是static
,然后将它们共享(我认为,我已经读过JVM并不能完全保证在DeviceCommunicator
的所有实例中,其他运行线程都可以看到静态变量更改-这是一个问题!
我已经进行了一些研究,但没有遇到类似的主题-大多数主题基本上都是“或”的,其中:
A)该主题是关于线程套接字通信的,其中“非阻塞”通信是通过使用静态变量来完成的。
或者
B)考虑一个简单的implements Runnable
情况,其中一个线程将完成一个(通常是简单的)任务,而另一个线程将完成另一个(通常是稍微修改的)任务。
编辑:我猜可能会提出一个解决方案,就是将输入的BufferedReader
传递给监听的DeviceCommunicator
线程,但是我正在实现要发送的消息队列(以防网络出现问题);因此,如果需要发送一条消息,它只是获取队列中的第一个元素并将其打印到套接字连接,我想在监听线程中验证该消息是否已被设备正确接收。如果正确接收到消息,那么我想从队列中删除元素,但是再次带来了问题-在Java中传递变量总是按值而不是按引用!因此,如果我要将输入的BufferedReader
和传递给队列,则在监听DeviceCommunicator
中正在修改的队列将不是需要在主DeviceCommunicator
实例中进行修改的实际队列。
我没有意识到这个问题的明显解决方案吗?
提前致谢!
最佳答案
如果您可以控制DeviceCommunicator
类,则可以将所需的Stream
或Reader
对象作为构造函数的参数传递。我当然不会以这种方式使用static
变量。
new Thread(new DeviceCommunicator(hostName, portNum, in, out)).start();
现在,如果有多个线程正在读取和写入流,则必须先在它们上进行同步。
您还在注释中提到需要发送一个
Queue
的消息。这也可能是DeviceCommunicator
的参数,尽管您将需要创建一个同步列表或其他内容:List<String> toSendList = Collections.synchronizedList(new ArrayList<String>());
...
new Thread(new DeviceCommunicator(hostName, portNum, in, out, toSendList)).start();
但是,更好的模式是在
send
上添加DeviceCommunicator
方法,该方法会将项目添加到队列中,而不是将主线程和发送线程都具有相同的队列。如果将方法添加到DeviceCommunicator
,则可能还可以隐藏读取器和写入器流,并且主线程将无法直接访问该流。 Data hiding是面向对象程序的重要功能之一。