java:将类引用传递给另一个线程

时间:2013-07-30 06:51:22

标签: java multithreading

我正在尝试学习如何在Java中使用多线程。我有一个main和两个扩展Thread,A和B的类。我希望main启动A,它会多次调用B.一旦A完成,我希望B向main发送一些东西。

main创建两个线程,一个A和一个B,然后启动两个线程。 A会做一些事情,然后将结果传递给B.然后主要从B收集答案并做其他事情。我不知道如何从B回到主要的总数。

我也不确定如何实例化两个类(线程),但是由于Java使用pass-by-value,因此给A引用了B。有人可以给我一些指示。

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

5 个答案:

答案 0 :(得分:3)

我将您的示例代码更改为我认为更有意义的示例。

线程之间的通信通常通过共享数据(或管道,套接字等通道来处理 - 但我不会去那里......)。虽然将这些共享数据包含在线程类中是完全正常的,但我已经从用于管理线程的数据/方法中分离了共享数据。

我希望这可以帮助您理解线程和数据对象之间的关系。

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

我知道这个天真的例子远非最佳,因为两个线程在等待设置/读取值时浪费了宝贵的周期。我只是希望尽可能简单地保持这个例子,同时还要解决我想要的问题。

答案 1 :(得分:0)

这是一个体面的方式。您只需传递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.
   }
}

然后你可以:

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

答案 2 :(得分:0)

首先,B不应该是一个线程。如果它只是响应来自A的消息,那么它就可以像任何其他对象一样。

其次,虽然每个人都说java使用pass by value,但令人困惑的部分是对象引用是按值传递的,因此对象通过引用有效传递。因此,您可以将B传递给A,并从B的副本中获取总数。

答案 3 :(得分:0)

如果我们查看您的代码:

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

您正在将b的指针传递给A。这意味着,如果class A仅直接访问class B(它不会将其替换为B的new实例),则该对象应包含class A对其执行的任何操作。换句话说,您的主要代码和class A都在同一个B对象上工作。因此,您应该只需询问已在b中实例化的对象,即可获得main的总数。

所以如果你打电话

b.total();

在main的末尾,它应该返回你的值(当然你必须确保线程A在检索值后不会对它进行更改)。

答案 4 :(得分:0)

我假设您正在尝试在main方法中创建的两个线程之间实现通信。然而,这并没有发生。我对您的代码进行了一些更改,并将其包含在下面,以显示我认为您想要执行的操作。

首先,对您的示例进行一些更正:

在引用线程对象时,不能像在main方法中那样使用类名(A和B)。请改用对象名称(a和b)。

在A类构造函数中,您正在创建一个新的局部变量 start ,而不是引用成员变量。因此:int start = n应为start = n

我猜你要循环在构造函数中设置的次数。因此for (int i = 0; i < n; i++) {应为for (int i = 0; i < start; i++) {

通过引用/值传递在这里并不重要。对象引用按值传递,就像其他任何东西一样。然而,它是有趣且不会改变的引用变量(对象地址)的内容。换句话说,当将对象引用传递给方法时,方法WILL将寻址该特定对象,并且对方法内容所做的任何更改也将在方法外部可见。

以下是您的示例,我认为您打算进行一些更正。

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

现在,这是这些例子的问题:

对象不是线程,反之亦然。在主线程中(让我们调用该线程tM)您正在创建一个B类对象并从其run()方法开始分叉一个新线程(线程tB)。但是,由于您未覆盖run方法,因此威胁在创建后立即结束。

然后创建一个A类对象。给它引用对象b(与线程tB无关)并分叉一个新线程(线程tA)。在这里,您实现了run()方法。结果如下:

线程tM完成了初始工作,现在正在等待线程tA完成。 线程tB开始后立即死亡 线程tA正在完成递增对象a的计数器的所有工作,并使对象b将计数器添加到其总和中。

当tA在100个增量之后完成时,tM唤醒并从对象b获取总和(这与线程tB无关)。