简体   繁体   English

这样可以处理线程和阻塞队列吗?

[英]Is this okay to do with threads and blocking queues?

I was wondering if it was okay that you give a thread access to an instance of a class so you can perform operations on certain members/variables of that class. 我想知道是否可以让线程访问类的实例,以便您可以对该类的某些成员/变量执行操作。

For example, I have both a main thread and a thread. 例如,我同时拥有一个主线程和一个线程。 I'm giving the second thread access to the instance of the main class so I can perform an operation on x. 我给第二个线程访问主类的实例,以便可以在x上执行操作。

However, what if at some point in the future I decide to do operations on x in the main thread? 但是,如果将来某个时候我决定对主线程中的x进行操作怎么办? Or just simply reading from x. 或者只是从x中读取。 What if both the other thread and the main thread want to read x at the same time? 如果另一个线程和主线程都想同时读取x怎么办?

Is this at all okay by the way I have it structured in my code to do? 通过在代码中进行结构化的方式,这完全可以吗?

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();
    }
}

And not just the member 'x', but also there could be more members in class "Test" that I'd want to be able to perform operations on such as reading/writing. 不仅是成员“ x”,而且在“测试”类中可能还希望有更多的成员能够执行读/写等操作。

Is this an okay structure to do so? 这样行吗? If not, what should be fixed? 如果没有,应该解决什么问题?

There are several problems with your code. 您的代码有几个问题。

Consider this line: 考虑这一行:

    aThread = new AThread(this);

It is always a bad idea to pass this somewhere in a constructor. 它始终是一个坏主意,通过this地方在构造函数中。 And this has nothing to do with the threads... yet. 这与线程无关...。 The reason is that the 'somewhere' may call a method on this , and the method could be overridden in a subclass whose constructor wasn't called yet, and it may end up in disaster because that override may use some of the subclass fields that aren't initialized yet. 其原因是,“地方”可以调用一个方法在this ,该方法可以在子类中它的构造当时尚未被称作被覆盖,也可能在灾难结束了,因为这可能会覆盖使用某些子类的字段是还没有初始化。

Now, when threads come into the picture, things get even worse. 现在,当线程进入画面时,情况变得更糟。 A thread is guaranteed to have correct access to a class instance that was created before the thread is started. 保证线程可以正确访问在线程启动之前创建的类实例。 But in your case, it isn't created yet, because the constructor is not finished yet! 但是在您的情况下,它尚未创建,因为构造函数尚未完成! And it's not going to finish anywhere soon because of the infinite loop below: 由于下面的无限循环,它不可能很快完成:

    while (true) {
        aThread.queue.put("up");

    }

So you have an object creation running in parallel to a startup of a thread. 因此,您具有与线程启动并行运行的对象创建。 Java doesn't guarantee that the thread will see the initialized class in such case (even if there was no loop). Java不保证在这种情况下该线程将看到初始化的类(即使没有循环)。

This is also one of the reasons why starting threads in constructors is considered a bad idea. 这也是为什么在构造函数中启动线程被认为是一个坏主意的原因之一。 Some IDEs even give a warning in such cases. 在这种情况下,某些IDE甚至会发出警告。 Note that running infinite loops in constructors is probably a bad idea too. 注意,在构造函数中运行无限循环可能也是一个坏主意。

If you move your code into a run() kind of method and do new Test().run() in main() , then you code will look fine, but you are right to worry about 如果将代码移到run()方法中,并在main()执行new Test().run() main() ,则代码看起来会很好,但是您可以担心

However, what if at some point in the future I decide to do operations on x in the main thread? 但是,如果将来某个时候我决定对主线程中的x进行操作怎么办?

The best idea is for the main thread to forget about the object right after it is passed to the thread: 最好的主意是让主线程在将对象传递给线程之后就忘记该对象:

public static void main(String[] args) throws InterruptedException {
    AThread aThread = new AThread(new Test());
    aThread.start();
    while (true) {
        aThread.queue.put("up");
    }
}

However, what if at some point in the future I decide to do operations on x in the main thread? 但是,如果将来某个时候我决定对主线程中的x进行操作怎么办? Or just simply reading from x. 或者只是从x中读取。 What if both the other thread and the main thread want to read x at the same time? 如果另一个线程和主线程都想同时读取x怎么办?

Any time you are sharing information between two threads, you need to provide for memory synchronization. 每当您在两个线程之间共享信息时,都需要提供内存同步。 In this case, if you make int x be volatile int x then your code should work fine. 在这种情况下,如果将int xvolatile int x则您的代码应该可以正常工作。 You should read the Java tutorial on the subject . 您应该阅读有关该主题Java教程

However, if the thread is doing more complex operations, as opposed to just setting or getting x , then you may need to make the method be synchronized or otherwise provide a mutex lock to make sure that the 2 threads don't overlap improperly. 但是,如果线程正在执行更复杂的操作,而不是仅设置或获取x ,那么您可能需要使方法synchronized或以其他方式提供互斥锁,以确保两个线程不会不正确地重叠。

For example, if you need to increment the value of x , a volatile won't help since increment is actually 3 operations: get, increment, and set. 例如,如果您需要增加x的值,则volatile将无济于事,因为增量实际上是3个操作:获取,增量和设置。 You could use a synchronized lock to protect the ++ or you should consider using an AtomicInteger which handles incrementAndGet() methods in a thread-safe manner. 您可以使用synchronized锁来保护++,或者应该考虑使用AtomicInteger ,该AtomicInteger以线程安全的方式处理incrementAndGet() AtomicInteger incrementAndGet()方法。

@Segey's answer gives some great feedback about the rest of your code. @Segey的答案为您的其余代码提供了很好的反馈。 I'll add one comment about this code: 我将对此代码添加一条评论:

    while (true) {
        aThread.queue.put("up");
    }

You almost never want to spin like this. 您几乎永远都不想这样旋转。 If you want to do something like this then I'd add some Thread.sleep(10) or something to slow down the adding to the queue or make the queue bounded in size. 如果您想做这样的事情,那么我将添加一些Thread.sleep(10)或一些减慢队列添加速度或使队列的大小受限的东西。 It is likely that you are going to run out of memory spinning and creating queue elements like this. 您可能会耗尽内存,无法像这样创建队列元素。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM