多线程java程序,可以交替打印偶数和奇数

时间:2015-11-06 05:51:58

标签: java multithreading synchronization

我被要求在一次采访中编写一个双线程的Java程序。在这个程序中,一个线程应该打印偶数,另一个线程应该交替打印奇数。

示例输出:

线程1:1

线程2:2

线程1:3

线程2:4 ......等等

我写了以下程序。一个类Task,其中包含两种分别打印偶数和奇数的方法。从main方法,我创建了两个线程来调用这两个方法。面试官让我进一步改进,但我想不出任何进步。有没有更好的方法来编写相同的程序?

class Task
{
    boolean flag;

    public Task(boolean flag)
    {
        this.flag = flag;
    }
    public void printEven()
    {
        for( int i = 2; i <= 10; i+=2 )
        {
            synchronized (this)
            {
                try
                {
                    while( !flag )
                        wait();
                    System.out.println(i);
                    flag = false;
                    notify();
                }
                catch (InterruptedException ex)
                {
                    ex.printStackTrace();
                }
            }
        }
    }
    public void printOdd()
    {
        for( int i = 1; i < 10; i+=2 )
        {
            synchronized (this)
            {
                try
                {
                    while(flag )
                        wait();
                    System.out.println(i);
                    flag = true;
                    notify();
                }
                catch(InterruptedException ex)
                {
                    ex.printStackTrace();
                }
            }
        }
    }
}

public class App {
    public static void main(String [] args)
    {
        Task t = new Task(false);
        Thread t1 = new Thread( new Runnable() {
           public void run()
           {
               t.printOdd();
           }
        });
        Thread t2 = new Thread( new Runnable() {
            public void run()
            {
                t.printEven();
            }
        });
        t1.start();
        t2.start();
    }
}

5 个答案:

答案 0 :(得分:2)

我认为这应该正常而且非常简单。

package com.simple;

import java.util.concurrent.Semaphore;

/**
 * @author Evgeny Zhuravlev
 */
public class ConcurrentPing
{
    public static void main(String[] args) throws InterruptedException
    {
        Semaphore semaphore1 = new Semaphore(0, true);
        Semaphore semaphore2 = new Semaphore(0, true);
        new Thread(new Task("1", 1, semaphore1, semaphore2)).start();
        new Thread(new Task("2", 2, semaphore2, semaphore1)).start();
        semaphore1.release();
    }

    private static class Task implements Runnable
    {
        private String name;
        private long value;
        private Semaphore semaphore1;
        private Semaphore semaphore2;

        public Task(String name, long value, Semaphore semaphore1, Semaphore semaphore2)
        {
            this.name = name;
            this.value = value;
            this.semaphore1 = semaphore1;
            this.semaphore2 = semaphore2;
        }

        @Override
        public void run()
        {
            while (true)
            {
                try
                {
                    semaphore1.acquire();
                    System.out.println(name + ": " + value);
                    value += 2;
                    semaphore2.release();
                }
                catch (InterruptedException e)
                {
                    throw new RuntimeException(e);
                }
            }
        }
    }

}

答案 1 :(得分:1)

嗯,有很多选择。我可能会使用SynchronousQueue代替(我不喜欢低级wait / notify并尝试使用更高级别的并发原语代替)。此外,printOddprintEven可以合并为单个方法,不需要其他标记:

public class App {
    static class OddEven implements Runnable {
        private final SynchronousQueue<Integer> queue = new SynchronousQueue<>();

        public void start() throws InterruptedException {
            Thread oddThread = new Thread(this);
            Thread evenThread = new Thread(this);
            oddThread.start();
            queue.put(1);
            evenThread.start();
        }

        @Override
        public void run() {
            try {
                while (true) {
                    int i = queue.take();
                    System.out.println(i + " (" + Thread.currentThread() + ")");
                    if (i == 10)
                        break;
                    queue.put(++i);
                    if (i == 10)
                        break;
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        new OddEven().start();
    }
}

答案 2 :(得分:0)

这样的短版本怎么样:

public class OddEven implements Runnable {
    private static volatile int n = 1;

    public static void main(String [] args) {
        new Thread(new OddEven()).start();
        new Thread(new OddEven()).start();
    }

    @Override
    public void run() {
        synchronized (this.getClass()) {
            try {
                while (n < 10) {
                    this.getClass().notify();
                    this.getClass().wait();
                    System.out.println(Thread.currentThread().getName() + ": " + (n++));
                    this.getClass().notify();
                }
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
        }
    }   
}

有一些技巧可以正确地启动线程 - 因此需要一个额外的notify()来启动整个事情(而不是让两个进程都等待,或者需要主线程调用一个通知)并且还处理线程启动的可能性,它是否正常工作并在第二个线程启动之前调用notify:)

答案 3 :(得分:0)

我的初步答案是无效的。编辑:

package test;

public final class App {

    private static volatile int counter = 1;
    private static final Object lock = new Object();

    public static void main(String... args) {
        for (int t = 0; t < 2; ++t) {
            final int oddOrEven = t;
            new Thread(new Runnable() {
                @Override public void run() {
                    while (counter < 100) {
                        synchronized (lock) {
                            if (counter % 2 == oddOrEven) {
                                System.out.println(counter++);
                            }
                        }
                    }
                }
            }).start();
        }
    }
}

答案 4 :(得分:0)

  

有没有更好的方法来编写相同的程序?

嗯,问题是,编写程序的唯一方法是使用单个线程。如果你想要一个程序按顺序执行X,Y和Z,那么编写一个执行X,然后是Y,然后执行Z的过程。没有比这更好的方法了。

这是我在与面试官讨论线索的适当性后我会写的内容。

import java.util.concurrent.SynchronousQueue;
import java.util.function.Consumer;

public class EvenOdd {
    public static void main(String[] args) {
        SynchronousQueue<Object> q1 = new SynchronousQueue<>();
        SynchronousQueue<Object> q2 = new SynchronousQueue<>();
        Consumer<Integer> consumer = (Integer count) -> System.out.println(count);
        new Thread(new Counter(q1, q2, 2, 1, consumer)).start();
        new Thread(new Counter(q2, q1, 2, 2, consumer)).start();
        try {
            q1.put(new Object());
        } catch (InterruptedException ex) {
            throw new RuntimeException(ex);
        }
    }

    private static class Counter implements Runnable {
        final SynchronousQueue<Object> qin;
        final SynchronousQueue<Object> qout;
        final int increment;
        final Consumer<Integer> consumer;
        int count;

        Counter(SynchronousQueue<Object> qin, SynchronousQueue<Object> qout,
                int increment, int initial_count,
                Consumer<Integer> consumer) {
            this.qin = qin;
            this.qout = qout;
            this.increment = increment;
            this.count = initial_count;
            this.consumer = consumer;
        }

        public void run() {
            try {
                while (true) {
                    Object token = qin.take();
                    consumer.accept(count);
                    qout.put(token);
                    count += increment;
                }
            } catch (InterruptedException ex) {
                throw new RuntimeException(ex);
            }
        }
    }
}