我正在尝试学习如何在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;
}
}
答案 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无关)。