3

我想重新启动一个线程以供某些用途,例如在下面的代码中。

class Ex13 implements Runnable {
    int i = 0;

    public void run() {
        System.out.println("Running " + ++i);
    }

    public static void main(String[] args) throws Exception {
        Thread th1 = new Thread(new Ex13(), "th1");
        th1.start();
            //th1.join() 
        Thread th2 = new Thread(th1);
        th2.start();
    }
}

当我执行上述程序时,有时我得到的输出为 Running 1 Running 2,有时我只得到 Running 1 经过几次运行后,我只得到 Running 1 作为输出。我对这种行为感到非常惊讶。任何人都可以帮助我理解这一点。如果我把 join() 那么我只得到 Running 1。

4

4 回答 4

2

我不知道你为什么需要这个,但是(请注意,这段代码并不能确保 th1 总是在 th2 之前执行):

public static class Ex13 implements Runnable {
    AtomicInteger i = new AtomicInteger(0);
    CountDownLatch latch;
    Ex13(CountDownLatch latch) {
        this.latch = latch;
    }
    public void run() {
        System.out.println("Running " + i.incrementAndGet());
        latch.countDown();
    }
}

public static void main(String[] args) throws Exception {
    CountDownLatch latch = new CountDownLatch(2);
    Ex13 r = new Ex13(latch);
    Thread th1 = new Thread(r, "th1");
    th1.start();
    Thread th2 = new Thread(r);
    th2.start();
    latch.await(); // wait until both theads are executed
    System.out.println("Done");
}
于 2012-06-30T09:30:39.447 回答
2

您重用Thread实例,而不是Runnable. 线程将其run()方法覆盖为

public void run() {
    if (target != null) {
        target.run();
    }
}

其中 target 是Runnable你给构造函数的那个​​。除此之外,Thread还有一个exit()由VM调用的方法,该方法将target设置为null(原因是这个bug)。所以如果你的第一个线程有机会完成它的执行,它的run()方法几乎是空的。添加th1.join()证明了这一点。

如果您想保留某些状态,则需要保留对您的Runnable实例的引用,而不是Thread. 这种方式run()方法不会改变。

于 2012-06-30T14:49:56.517 回答
0

您希望 i 的递增同步,即

public class Ex13 implements Runnable {
    int i=0;
    public void run() {
        System.out.println("Running "+ increment());
    }
    private synchronized int increment() {
        return ++i;
    }
}

Java 教程给出了一个非常相似的场景对此进行了很好的解释。问题是递增变量不是原子操作。每个线程都需要先读取 i 的当前状态,然后再将其设置为新值。限制一次将变量递增到一个线程的访问可确保您获得一致的行为。

于 2012-06-30T09:22:13.363 回答
0

要查看 System.out.println 中发生的情况,您还可以打印线程名称:

Thread t = Thread.currentThread();
String name = t.getName();
System.out.println("name=" + name);

我看到您使用相同的可运行对象调用两个线程,因此它们都将使用相同的“i”变量,为了让您获得 Running 1 Running 2,您需要同步“i”

于 2012-06-30T10:02:16.640 回答