0

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

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

我也不确定如何实例化这两个类(线程),然后给 A 一个 B 的引用,因为 Java 使用按值传递。有人可以给我一些指示。

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

5 回答 5

5

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

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

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

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

我知道这个幼稚的例子远非最佳,因为两个线程在等待设置/读取值时都在浪费宝贵的周期。我只是想让这个例子尽可能简单,同时仍然解决我想要表达的观点。

于 2013-07-30T13:26:14.243 回答
0

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

其次,虽然大家都说java使用传值,但令人困惑的是对象引用是传值,所以对象实际上是传引用。因此,您可以将 B 传递给 A,并从您的 B 副本中获取总数。

于 2013-07-30T06:56:57.903 回答
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++) {

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

这是您的示例,并按我认为您的意图进行了一些更正。

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 无关)。

于 2013-07-30T11:35:40.513 回答
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

您正在传递bto的指针A。这意味着如果class Aclass B直接访问(它不会用newB 的实例替换它)该对象应该包含class A对它所做的任何事情。换句话说,您的主要代码class AB. 所以你应该能够b简单地通过询问你已经实例化的对象来获得总数main

所以如果你打电话

b.total();

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

于 2013-07-30T06:58:09.150 回答
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();
于 2013-07-30T06:53:27.553 回答