正确实现乒乓游戏

时间:2014-04-10 19:36:48

标签: java concurrency semaphore reentrantlock countdownlatch

我被要求执行一个名为“ping”和“pong”的pingpong游戏(意思是ping之前没有pong)10次。意思是,控制台中的最终输出应该是:“ping!(1)”,“pong!(1)”,“ping!(2)”,“pong!(2)”等。

需求是用信号量,reetrantlock和倒计时锁定实现gamepingpongthread。

我的问题是打印订单并不总是如我所要求的那样,我想知道我做错了什么。

以下是代码:

// Import the necessary Java synchronization and scheduling classes.
import java.util.concurrent.Semaphore;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Condition;

/**
 * @class PingPongRight
 *
 * @brief This class implements a Java program that creates two
 *        instances of the PlayPingPongThread and start these thread
 *        instances to correctly alternate printing "Ping" and "Pong",
 *        respectively, on the console display.
 */
public class PingPongRight
{
    /**
     * @class SimpleSemaphore
     *
     * @brief This class provides a simple counting semaphore
     *        implementation using Java a ReentrantLock and a
     *        ConditionObject.
     */
    static public class SimpleSemaphore
    {
        private int mPermits;
        private ReentrantLock lock = new ReentrantLock();
        private Condition isZero = lock.newCondition();

        /**
         * Constructor initialize the data members. 
         */
        public SimpleSemaphore (int maxPermits)
        { 
            mPermits = maxPermits;
        }

        /**
         * Acquire one permit from the semaphore.
         */
        public void acquire() throws InterruptedException
        {
            lock.lock();
            while (mPermits == 0)
                isZero.await();
            mPermits--;
            lock.unlock();
        }

        /**
         * Return one permit to the semaphore.
         */
        void release() throws InterruptedException
        {
            lock.lock();
            try {
                mPermits++;
                isZero.signal();
            } finally {
                lock.unlock();
            }
        }
    }

    /**
     * Number of iterations to run the test program.
     */
    public static int mMaxIterations = 10;

    /**
     * Latch that will be decremented each time a thread exits.
     */
    public static CountDownLatch latch = new CountDownLatch(2);

    /**
     * @class PlayPingPongThread
     *
     * @brief This class implements the ping/pong processing algorithm
     *         using the SimpleSemaphore to alternate printing "ping"
     *         and "pong" to the console display.
     */
    public static class PlayPingPongThread extends Thread
    {
        private String message;
        private SimpleSemaphore semaphore;

        /**
         * Constructor initializes the data member.
         */
        public PlayPingPongThread (String msg, SimpleSemaphore pingOrPong)
        {
            message = msg;
            semaphore = pingOrPong;
        }

        /**
         * Main event loop that runs in a separate thread of control
         * and performs the ping/pong algorithm using the
         * SimpleSemaphores.
         */
        public void run () 
        {
            for (int i = 1 ; i <= mMaxIterations ; i++) {
                try {
                    semaphore.acquire();
                    System.out.println(message + "(" + i + ")");
                    semaphore.release();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            latch.countDown();
        }
    }

    /**
     * The main() entry point method into PingPongRight program. 
     */
    public static void main(String[] args) {
        try {         
            // Create the ping and pong SimpleSemaphores that control
            // alternation between threads.
            SimpleSemaphore pingSemaphore = new SimpleSemaphore(mMaxIterations);
            SimpleSemaphore pongSemaphore = new SimpleSemaphore(mMaxIterations);

            System.out.println("Ready...Set...Go!");

            // Create the ping and pong threads, passing in the string
            // to print and the appropriate SimpleSemaphores.
            PlayPingPongThread ping = new PlayPingPongThread("Ping!", pingSemaphore);
            PlayPingPongThread pong = new PlayPingPongThread("Pong!", pongSemaphore);

            // Initiate the ping and pong threads, which will call the run() hook method.
            ping.start();
            pong.start();

            // Use barrier synchronization to wait for both threads to finish.
            latch.await();
        } 
        catch (java.lang.InterruptedException e)
            {}

        System.out.println("Done!");
    }
}

提前致谢

2 个答案:

答案 0 :(得分:8)

  

我的问题是打印订单并不总是如我所要求的那样,我想知道我做错了什么。

我认为你的问题是ping和pong线程都在获取并释放自己的信号量。我认为您需要将两个信号量传递给两个线程。每个帖子都会调用acquire()上的acquireSemaphorerelease()上的releaseSemaphore

  acquireSemaphore.acquire();
  System.out.println(message + "(" + i + ")");
  releaseSemaphore.release();

线程看起来像:

public PlayPingPongThread (String msg, SimpleSemaphore acquireSemaphore,
        SimpleSemaphore releaseSemaphore)

然后将线程初始化为:

// ping acquires on the ping, releases the pong
PlayPingPongThread ping = new PlayPingPongThread("Ping!", pingSemaphore, pongSemaphore);
// pong acquires on the pong, releases the ping
PlayPingPongThread pong = new PlayPingPongThread("Pong!", pongSemaphore, pingSemaphore);

pingSemaphore应该从1开始,而pong应该从0开始。

  1. ping首先致电acquire()上的pingSemaphore,然后就会给出。
  2. ping打印出ping。
  3. pingrelease()上调用pongSemaphore
  4. 这会唤醒pong(假设您的信号量代码当然有效)。
  5. pong打印pong
  6. pongrelease()上调用pingSemaphore
  7. 重复...

答案 1 :(得分:0)

您还可以使用“重入”锁定条件来实现乒乓游戏,这与等待通知非常相似。

package com.example.thread;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class PingPongUsingReentrantCondition {

    private static ReentrantLock lock = new ReentrantLock(true);
    private Condition conditionMet = lock.newCondition();

    public static void main(String[] args) {
        PingPongUsingReentrantCondition pingPong = new PingPongUsingReentrantCondition();
        int times = 10;
        Thread t1 = new Thread(() -> pingPong.pingpong("Ping!", times));
        Thread t2 = new Thread(() -> pingPong.pingpong("Pong!", times));
        t1.start();
        t2.start();
    }
    
    public void pingpong(String s, int times) {
        int counter = 1;
        while(counter<=times) {             
            run(s, counter);
            counter = counter+1;
        }
    }

    public void run(String s, int counter) {
        lock.lock();
        try {
            conditionMet.await(2, TimeUnit.SECONDS);
            System.out.println(s + "(" + counter + ")");
            conditionMet.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}