简体   繁体   中英

java: Passing a class reference to another thread

I'm trying to learn how to use multithreading in Java. I have a main and two classes which extend Thread, A and B. I want the main to start A, which makes multiple calls to B. Once A is finished, I want B to send something to main.

The main creates two threads, one A and one B, and then starts both threads. A does something, which will then pass on a result to B. The main then collects an answer from B and does something else. I don't know how to get the total from B back to the main.

I'm also not sure how to instantiate the two classes (threads) but then give A a reference of B since Java uses pass-by-value. Can someone give me some pointers.

public static void main(String[] args)
{
    B b = new B();
    A a = new A(100, b);

    B.start();
    A.start();

    A.join(); // Waiting for A to die

    // Here I want to get a total from B, but I'm not sure how to go about doing that

}


public class A extends Thread
{
    private int start;
    // Some reference to B
    B b;
    public A (int n, B b) {
        int start = n;
        this.b = b;
    }

    public void run() {
        for (int i = 0; i < n; i++) {
            b.add(i);
        }
    }
}

public class B extends Thread
{
    private int total;

    public B () {
        total = 0;
    }

    public void add(int i) {
        total += i;
    }
}

I changed your example code into what I consider to be a more meaningful example.

Communication between threads is usually handled through shared data (or channels like pipes, sockets - but I wont go there...). And while it is perfectly alright to have this shared data contained within the thread classes I have seperated the shared data from the data/methods used to administer the threads.

I hope this helps you to understand the relationship between threads and data objects.

public class TestThreads {
    public static void main(String[] args)
    {
        DataShare ds = new DataShare();
        B b = new B(ds);
        A a = new A(100, ds);

        b.start();
        a.start();

        try {
            a.join(); // Waiting for A to die
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println ("Accumulated total from B = " + b.getTotal());      
        b.endThread();
    }   
}


public class DataShare {
    private int value;

    public DataShare () {
        value = -1;
    }

    public synchronized boolean setValue(int val) {
        boolean valueSet = false;
        if (value == -1) {
            value = val;
            valueSet = true;
        }
        return valueSet;        
    }

    public synchronized int getValue() {
        int val = value;
        value = -1;
        return val;
    }    
}


public class A extends Thread {
    private int max;
    private DataShare dataShare;

    public A (int n, DataShare ds) {
        max = n;
        dataShare = ds;
    }

    public void run() {
        int i = 0;
        while (i < max) {
            if (dataShare.setValue(i)) {
                i++;
            }
        }
    }
}


public class B extends Thread {
    private int total;
    private DataShare dataShare;
    private boolean running = false;

    public B (DataShare ds) {
        dataShare = ds;
        total = 0;
    }

    public void run() {
        running = true;
        while (running) {
            int nextValue = dataShare.getValue();
            if (nextValue != -1) {
                total += nextValue;
            }
        }
    }

    public int getTotal() {
        return total;
    }

    public synchronized void endThread() {
        running = false;
    }
}

I am aware that this naive example is far from optimal since both threads are wasting precious cycles while waiting for the value to be set/read. I just wanted to keep the example as simple as possible while still addressing the point I'm trying to make.

A decent way to do it. You just pass an instance of class A:

public class Foo {
   public void doFoo() {..} // that's the method you want to use
}

public class Bar {
   private Foo foo;
   public Bar(Foo foo) {
      this.foo = foo;
   }

   public void doSomething() {
      foo.doFoo(); // here you are using it.
   }
}

And then you can have:

Foo foo = new Foo();
Bar bar = new Bar(foo);
bar.doSomething();

First of all, B should not be a thread. If all it does is respond to messages from A, then it can just be an object like any other.

Second, while everyone says java uses pass by value, the confusing part is that object references are passed by value, so objects are effectively passed by reference. Thus, you can pass B to A, and get the total from your copy of B.

If we look at your code:

B b = new B();
A a = new A(100, b);

B.start();
A.start();

A.join(); // Waiting for A to die

// Here I want to get a total from B, but I'm not sure how to go about doing that

You are passing a pointer of b to A . That means that if class A only accesses class B directly (it does not replace it with a new instance of B) that the object should contain anything that class A did to it. In other words, both your main code and class A work on the same object of B . So you should be able to get the total of b simply by asking the object you have instantiated in main .

So if you call

b.total();

at the end of main, it should return your value (of course you must ensure that thread A will not make changes to it after retrieving the value).

I assume that you are trying to achieve communication between the two threads you create in the main method. This is, however, not happening. I made a few changes to your code and included it below to show what I think you wanted to do.

First, a few corrections of your examples:

You cannot use class names (A and B) when referencing thread objects as you do in the main method. Use the object names instead (a and b).

In class A constructor you are creating a new local variable start instead of referencing the member variable. Hence: int start = n should be start = n

I'm guessing you want to loop the number of times set in constructor. Hence for (int i = 0; i < n; i++) { should be for (int i = 0; i < start; i++) {

Pass by reference/value is not really relevant here. Object references are passed by value as anything else is. It is, however, the contents of the reference variable (the object address) which is interesting and that will not change. In other words, when passing an object reference to a method, the method WILL address that specific object, and any change made to the contents of the objects will be visible outside the method as well.

Here are your examples with a few corrections as I think you intended them.

public class TestThreads {
        public static void main(String[] args)
        {
            B b = new B();
            A a = new A(100, b);

            b.start();
            a.start();

            try {
                a.join(); // Waiting for A to die
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            // Getting a total from b is simple, if you provide the method for it
                System.out.println ("Accumulated total from B = " + b.getTotal());      
        }

}


public class A extends Thread {
    private int start;
    // Some reference to B
    B b;
    public A (int n, B b) {
        start = n;
        this.b = b;
    }

    public void run() {
        for (int i = 0; i < start; i++) {
            b.add(i);
        }
    }
}


public class B extends Thread {
    private int total;

    public B () {
        total = 0;
    }

    public void add(int i) {
        total += i;
    }

    public int getTotal() {
        return total;
    }
}

Now, here is the problem with these examples:

An object is not a thread and vice versa. In the main thread (lets call that thread tM) You are creating an object of class B and forking a new thread (thread tB) starting in its run() method. However, since you did not override the run method, the threat ends immediately after creation.

Then you create an object of class A. Gives it the reference to object b (which has nothing to do with thread tB) and fork a new thread (thread tA). Here you did implement a run() method. The result is the following:

Thread tM did the initial work and is now waiting for thread tA to finish. Thread tB was started and died immediately afterwards Thread tA is doing all the work of incrementing the counter of object a, and making object b add the counter to its sum.

When tA finished after 100 increments tM wakes up and acquires the sum from object b (which again has nothing to do with thread tB).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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