我想知道你是否可以给一个线程访问一个类的实例 所以你可以对那个类的某些成员/变量进行操作。
例如,我有一个主线程和一个线程。 我让第二个线程访问主类的实例 所以我可以在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();
}
}
不仅仅是会员&#39; x&#39;,而且课堂上可能会有更多会员&#34;测试&#34;我希望能够执行阅读/写作等操作。
这是一个好的结构吗?如果没有,应该修复什么?
答案 0 :(得分:2)
您的代码存在一些问题。
考虑这一行:
aThread = new AThread(this);
在构造函数中的某处传递this
总是一个坏主意。而这与线程无关......原因是'某处'&#39;可以在this
上调用一个方法,并且该方法可以在其构造函数尚未被调用的子类中被覆盖,并且它可能最终处于灾难状态,因为该覆盖可能会使用某些子类字段,而这些字段不是&# 39; t初始化。
现在,当线程出现时,事情变得更糟。保证线程可以正确访问在线程启动之前创建的类实例。但是在你的情况下,它还没有被创建,因为构造函数还没有完成!由于下面的无限循环,它不会很快完成:
while (true) {
aThread.queue.put("up");
}
所以你有一个对象创建与一个线程的启动并行运行。 Java并不保证线程在这种情况下会看到初始化的类(即使没有循环)。
这也是为什么在构造函数中启动线程被认为是个坏主意的原因之一。有些IDE甚至会在这种情况下发出警告。请注意,在构造函数中运行无限循环也可能是一个坏主意。
如果您将代码转换为run()
种方法并在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");
}
}
答案 1 :(得分:1)
但是,如果在将来的某个时候我决定在主线程中对x进行操作呢?或者只是简单地从x读取。如果另一个线程和主线程同时想要读取x会怎么样?
每次在两个线程之间共享信息时,都需要提供内存同步。在这种情况下,如果您将int x
设为volatile int x
,那么您的代码应该可以正常运行。您应该阅读Java tutorial on the subject。
但是,如果线程正在执行更复杂的操作,而不是仅设置或获取x
,那么您可能需要使方法为synchronized
或以其他方式提供互斥锁以确保2个线程没有不正确地重叠。
例如,如果您需要增加x
的值,volatile
将无济于事,因为增量实际上是3个操作:get,increment和set。您可以使用synchronized
锁来保护++,或者您应该考虑使用AtomicInteger
以线程安全的方式处理incrementAndGet()
方法。
@ Segey的回答提供了关于其余代码的一些很好的反馈。我将添加一条关于此代码的评论:
while (true) {
aThread.queue.put("up");
}
你几乎不想像这样旋转。如果你想做这样的事情,那么我会添加一些Thread.sleep(10)
或者某些东西来减缓添加到队列或使队列的大小有限。你很可能会耗尽内存并创建像这样的队列元素。