Java线程同步 - 以正确的顺序打印数字

时间:2011-04-11 09:57:31

标签: java multithreading synchronization

我正在学习如何使用Java中的线程,我需要一些建议..

我想在0..50的标准输出数字上打印使用三个线程完成它的线程的名称。

我有两个类 - 类计数器,用于实现Runnable和类Main ,用于创建和运行线程。 Counter具有变量 c ,它在线程之间共享。

我的想法是,我将 c 增加1,然后在当前线程上调用 yield(),以便其他线程也这样做。重复此操作,直到 c 达到50。

但它不起作用,数字按错误的顺序打印出来。我该如何解决这个问题?

public class Counter implements Runnable {

Thread t1;
private int c = -1;

public Counter() {
}

public Counter(String name) {
    t1 = new Thread(this, name);
    t1.start();
}

@Override
public void run() {
    while (c < 50) {
        increment();
        Thread.yield();
    }
}

public void increment() {
    if (c < 50) {
        c++;
        System.out.println(Thread.currentThread().getName() + ": " + c);
    }
}
}

public class Main {

public static void main(String[] args) throws IllegalThreadStateException {
    Counter c1 = new Counter();
    Thread t1 = new Thread(c1, "Thread 1");
    Thread t2 = new Thread(c1, "Thread 2");
    Thread t3 = new Thread(c1, "Thread 3");

    t1.start();
    t2.start();
    t3.start();

}

编辑:最后我用这种方式解决了。感谢所有帮助我开始多线程的人。

import java.util.concurrent.atomic.AtomicInteger;

public class Counter2 implements Runnable {

// you could also use simple int
private AtomicInteger c = new AtomicInteger(-1);
private static final Object syncObject = new Object();

public Counter2() {
}

@Override
public void run() {
    while (c.get() < 50) {
        synchronized (syncObject) {
            if (c.get() < 50) {
                System.out.println(Thread.currentThread().getName() + ": " + c.incrementAndGet());
            }
        }
    }
}    
}

4 个答案:

答案 0 :(得分:2)

在方法增量中使用syncrhonized部分和特殊的静态对象。

private static final Object syncObj = new Object();

public void increment()
{
  syncrhonized( syncObj )
  {
   c++;
   System.out.println(c);
  }
}

或者通过声明使这个方法同步。

但是将真实数据存储在线程对象中是错误的。线程应该只使用共享对象进行操作,而不是存储它们。 实际上我不明白你为什么要开始使用

答案 1 :(得分:1)

使increment()同步,以防止其他线程同时进入该方法。

yield()一起你应该能够让另一个线程打印下一个数字(并不总是因为系统可能会再次恢复调用yield的线程 - 请参阅Ingo的答案 - 但是顺序应该仍然是相同的)。

synchronized increment()意味着任何试图在同一个对象上输入该方法的线程都必须等待,如果另一个线程已经通过输入方法获得了锁。

答案 2 :(得分:1)

引用javadoc Thread.yield(),我强调:

public static void yield()
  

提示调度程序   目前的线程愿意屈服于它   当前使用的处理器。的的   调度程序可以自由地忽略它   提示

     

...

     

很少适合使用   这种方法。

答案 3 :(得分:1)

是的,您的代码无效。 Thread#yield()不会以您希望的方式控制线程调度程序。我很好奇你得到了什么结果。你可能会得到重复的数字和一些稍微不正常的数字。

您可以使用原子整数来删除所有重复项。但由于print语句不是原子的。您仍可能无序打印结果。所以你应该只是同步增量方法。你也不需要收益,所以转储它。

如果问题的目的是从线程1到线程2再到线程3回到线程1等等......结果是

Thread 1:0
Thread 2:1
Thread 3:2
Thread 1:3
Thread 2:4
Thread 3:5
Thread 1:6
Thread 2:7
....

然后你需要锁定increment方法并使用wait和notifyAll。 wait将导致其他线程暂停处理,直到当前线程通知它们再次启动。