簡體   English   中英

多線程java程序,可以交替打印偶數和奇數

[英]Multi threaded java program to print even and odd numbers alternatively

我被要求在一次采訪中編寫一個雙線程的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();
    }
}

我認為這應該正常,非常簡單。

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

}

嗯,還有很多選擇。 我可能會使用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();
    }
}

這樣的短版本怎么樣:

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 :)

我的初步答案是無功能的。 編輯:

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

有沒有更好的方法來編寫相同的程序?

嗯,問題是,編寫程序的唯一方法是使用單個線程。 如果你想要一個程序按順序執行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);
            }
        }
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM