java:使用2个线程打印奇数偶数

时间:2013-03-03 04:21:27

标签: java multithreading

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

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

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

}

有什么想法吗?

我根据布鲁诺的建议创建了另一个版本,似乎效果更好:

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

3 个答案:

答案 0 :(得分:3)

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

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

  1. 第一个主题看到chk == true,将其设置为false并输入if块。
  2. 第二个主题看到chk == false,将其设置为true并输入if块。
  3. 现在,你的if块中有2个线程,准备好了:

    1. incrementAndGet()数字
    2. 打印出来。
    3. 因此,你完全无法控制将会发生什么。

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

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

      • compareAndSet()布尔
      • incrementAndGet()数字
      • 打印

      如果3个操作不是原子操作,那么您可以调度线程以任何可能的顺序运行操作,您无法控制输出。实现此目的的最简单方法是使用synchronized块:

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

答案 1 :(得分: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);
            }
        }
    }
}

答案 2 :(得分: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();