[英]Is this okay to do with threads and blocking queues?
我想知道是否可以让线程访问类的实例,以便您可以对该类的某些成员/变量执行操作。
例如,我同时拥有一个主线程和一个线程。 我给第二个线程访问主类的实例,以便可以在x上执行操作。
但是,如果将来某个时候我决定对主线程中的x进行操作怎么办? 或者只是从x中读取。 如果另一个线程和主线程都想同时读取x怎么办?
通过在代码中进行结构化的方式,这完全可以吗?
package test;
import java.lang.Thread;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
class AThread extends Thread {
Test test;
AThread(Test test) {
this.test = test;
}
BlockingQueue<String> queue = new LinkedBlockingQueue<String>();
public void run() {
String msg;
while ((msg = queue.poll()) != null) {
// Process the message
//System.out.println(msg); //should print "hello"
if (msg.equals("up")) {
test.setX(test.getX()+1);
System.out.println(test.getX());
}
}
}
}
public class Test {
AThread aThread;
private int x = 5;
void setX(int x){
this.x = x;
}
int getX(){
return x;
}
Test() throws InterruptedException{
System.out.println("MainThread");
aThread = new AThread(this);
aThread.start();
while (true) {
aThread.queue.put("up");
}
}
public static void main(String[] args) throws InterruptedException {
new Test();
}
}
不仅是成员“ x”,而且在“测试”类中可能还希望有更多的成员能够执行读/写等操作。
这样行吗? 如果没有,应该解决什么问题?
您的代码有几个问题。
考虑这一行:
aThread = new AThread(this);
它始终是一个坏主意,通过this
地方在构造函数中。 这与线程无关...。 其原因是,“地方”可以调用一个方法在this
,该方法可以在子类中它的构造当时尚未被称作被覆盖,也可能在灾难结束了,因为这可能会覆盖使用某些子类的字段是还没有初始化。
现在,当线程进入画面时,情况变得更糟。 保证线程可以正确访问在线程启动之前创建的类实例。 但是在您的情况下,它尚未创建,因为构造函数尚未完成! 由于下面的无限循环,它不可能很快完成:
while (true) {
aThread.queue.put("up");
}
因此,您具有与线程启动并行运行的对象创建。 Java不保证在这种情况下该线程将看到初始化的类(即使没有循环)。
这也是为什么在构造函数中启动线程被认为是一个坏主意的原因之一。 在这种情况下,某些IDE甚至会发出警告。 注意,在构造函数中运行无限循环可能也是一个坏主意。
如果将代码移到run()
方法中,并在main()
执行new Test().run()
main()
,则代码看起来会很好,但是您可以担心
但是,如果将来某个时候我决定对主线程中的x进行操作怎么办?
最好的主意是让主线程在将对象传递给线程之后就忘记该对象:
public static void main(String[] args) throws InterruptedException {
AThread aThread = new AThread(new Test());
aThread.start();
while (true) {
aThread.queue.put("up");
}
}
但是,如果将来某个时候我决定对主线程中的x进行操作怎么办? 或者只是从x中读取。 如果另一个线程和主线程都想同时读取x怎么办?
每当您在两个线程之间共享信息时,都需要提供内存同步。 在这种情况下,如果将int x
为volatile int x
则您的代码应该可以正常工作。 您应该阅读有关该主题的Java教程 。
但是,如果线程正在执行更复杂的操作,而不是仅设置或获取x
,那么您可能需要使方法synchronized
或以其他方式提供互斥锁,以确保两个线程不会不正确地重叠。
例如,如果您需要增加x
的值,则volatile
将无济于事,因为增量实际上是3个操作:获取,增量和设置。 您可以使用synchronized
锁来保护++,或者应该考虑使用AtomicInteger
,该AtomicInteger
以线程安全的方式处理incrementAndGet()
AtomicInteger
incrementAndGet()
方法。
@Segey的答案为您的其余代码提供了很好的反馈。 我将对此代码添加一条评论:
while (true) {
aThread.queue.put("up");
}
您几乎永远都不想这样旋转。 如果您想做这样的事情,那么我将添加一些Thread.sleep(10)
或一些减慢队列添加速度或使队列的大小受限的东西。 您可能会耗尽内存,无法像这样创建队列元素。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.