这可以用线程和阻塞队列吗?

时间:2014-04-04 16:30:16

标签: java multithreading

我想知道你是否可以给一个线程访问一个类的实例 所以你可以对那个类的某些成员/变量进行操作。

例如,我有一个主线程和一个线程。 我让第二个线程访问主类的实例 所以我可以在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;我希望能够执行阅读/写作等操作。

这是一个好的结构吗?如果没有,应该修复什么?

2 个答案:

答案 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)或者某些东西来减缓添加到队列或使队列的大小有限。你很可能会耗尽内存并创建像这样的队列元素。