0

我正在尝试交替使用 2 个不同的线程打印奇数和偶数。我能够使用等待、通知和同步块来实现它,但现在我想评估我们是否可以在不使用等待、通知和同步的情况下实现它。

以下是我有但它不起作用的代码:

public class OddEvenUsingAtomic {

AtomicInteger nm = new AtomicInteger(0);
AtomicBoolean chk = new AtomicBoolean(true);

public static void main(String args[]) {
    final OddEvenUsingAtomic pc = new OddEvenUsingAtomic();

    new Thread(new Runnable() {

        @Override
        public void run() {
            while (true) {

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }

                if (pc.chk.compareAndSet(true, false)) {

                    System.out.println("Odd: " + pc.nm.incrementAndGet());
                }
            }

        }

    }).start();

    new Thread(new Runnable() {

        @Override
        public void run() {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            while (true) {
                if (pc.chk.compareAndSet(false, true)) {

                    System.out.println("Even: " + pc.nm.incrementAndGet());
                }
            }

        }

    }).start();
}

}

有任何想法吗?

在 Bruno 的建议下,我创建了另一个版本,似乎效果更好:

import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

public class OddEvenUsingAtomic {

    AtomicInteger nm = new AtomicInteger(0);
    AtomicBoolean chk = new AtomicBoolean(true);

    public static void main(String args[]) {
        final OddEvenUsingAtomic pc = new OddEvenUsingAtomic();

        new Thread(new Runnable() {

            @Override
            public void run() {
                while (true) {

                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }

                    if (pc.chk.get() == Boolean.TRUE) {

                        System.out.println("Odd: " + pc.nm.incrementAndGet());
                        pc.chk.compareAndSet(true, false);
                    }
                }

            }

        }).start();

        new Thread(new Runnable() {

            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                while (true) {
                    if (pc.chk.get() == Boolean.FALSE) {

                        System.out.println("Even: " + pc.nm.incrementAndGet());
                        pc.chk.compareAndSet(false, true);
                    }
                }

            }

        }).start();
    }
}
4

3 回答 3

3

代码没有正确同步,这就是问题所在。

您的代码中允许以下执行顺序:

  1. 第一个线程看到chk == true,将其设置为false并进入if块。
  2. 第二个线程看到chk == false,将其设置为true并进入if块。

现在,您的块中都有 2 个线程if,准备:

  1. incrementAndGet()号码
  2. 打印它。

因此,您完全无法控制将要发生的事情。

  • 您可以让任何线程调用incrementAndGet(),因此您可以让线程“奇数”打印,首先打印一个奇数,然后打印一个偶数。
  • 您可以让第一个线程打印数字,循环,看看条件再次满足(因为第二个线程再次设置chktrue打印,所有这些都在第二个线程有机会打印之前)。

如您所见,要达到您想要的结果,您必须以原子方式完成以下操作:

  • compareAndSet()布尔值
  • incrementAndGet()号码
  • 打印它

如果这 3 个操作不是原子的,那么您可以安排线程以任何可能的顺序运行操作,您无法控制输出。实现这一点的最简单方法是使用同步块:

public static void main(final String... args) {
  final Object o = new Object();
  // ... thread 1 ...
    synchronized(o) {
      if (boolean is in the expected state) { change boolean, get number, increment, print }
    }
  // ... thread 2 ...
    synchronized(o) {
      if (boolean is in the expected state) { change boolean, get number, increment, print }
    }
}
于 2013-03-03T04:42:04.827 回答
3

这里有两个线程打印奇数和偶数,没有等待、通知或同步(至少在您可以看到的代码中没有):

import java.util.concurrent.*;

public class ThreadSignaling {
    public static void main(String[] args) {
        BlockingQueue<Integer> evens = new LinkedBlockingQueue<>();
        BlockingQueue<Integer> odds = new LinkedBlockingQueue<>();
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        executorService.submit(() -> takeAndOfferNext(evens, odds));
        executorService.submit(() -> takeAndOfferNext(odds, evens));
        evens.offer(0);
    }

    private static void takeAndOfferNext(BlockingQueue<Integer> takeFrom,
                                         BlockingQueue<Integer> offerTo) {
        while (true) {
            try {
                int i = takeFrom.take();
                System.out.println(i);
                offerTo.offer(i + 1);
            } catch (InterruptedException e) {
                throw new IllegalStateException("Unexpected interrupt", e);
            }
        }
    }
}
于 2013-03-03T05:09:51.083 回答
0
class MultiThreading {

    Integer counter = 0;
    Thread even;
    Thread odd;
    boolean flagEven = true;
    boolean flagOdd;

    class ThreadEven implements Runnable {
        @Override
        public void run() {
            try {
                while (counter < 100) {
                    if (flagEven) {
                        System.out.println(counter);
                        counter++;
                        flagEven = false;
                        flagOdd = true;
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    class ThreadOdd implements Runnable {
        @Override
        public void run() {
            try {
                synchronized (even) {
                    while (counter < 100) {
                        if (flagOdd) {
                            System.out.println(counter);
                            counter++;
                            flagOdd = false;
                            flagEven = true;
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public void start() {
        even = new Thread(new ThreadEven());
        odd = new Thread(new ThreadOdd());
        even.start();
        odd.start();
    }

}

}

在main方法中调用

new MultiThreading().start();
于 2017-06-22T10:32:35.570 回答