java - 正确实现乒乓球游戏

标签 java concurrency semaphore reentrantlock countdownlatch

我在一项作业中被要求实现 10 次正确调用“ping”和“pong”(意思是在 ping 之前没有 pong)的乒乓球游戏。也就是说,控制台的最终输出应该是:“ping!(1)”、“pong!(1)”、“ping!(2)”、“pong!(2)”等。

需求是用semaphores、reetrantlock和countdown latch实现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!");
    }
}

提前致谢

最佳答案

My problem is that the print order is not always as requested, and I wonder what I'm doing wrong.

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

  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 permit 开始,而 pong 应该从 0 开始。

  1. ping 首先调用 pingSemaphore 上的 acquire() 并给出它。
  2. ping 打印出 ping。
  3. pingpongSemaphore 上调用 release()
  4. 这会唤醒 pong(当然假设您的信号量代码有效)。
  5. pong 打印 pong
  6. pongpingSemaphore 上调用 release()
  7. 重复...

关于java - 正确实现乒乓球游戏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22997434/

相关文章:

concurrency - Rust 异步等待 : check if any future in a list resolves to true concurrently?

c - 将程序与信号量同步

java - 如何在另一个java程序中使用cmd打开/运行java程序

java - 使用 RestTemplate 获取模型对象

java - Kafka 消费者 - 暂停对特定 Kafka 主题分区的事件轮询,以将其用作延迟队列

java - 在 while 循环中等待(长时间超时)?

sql-server - 即使没有显式事务,SQL Server 是否也获取锁?

c# - 为什么我需要将锁与信号量一起使用

swift - 使用信号量从解析中获取图像时主线程被锁定

java - 如何在 Java 中设置 LDAP 客户端请求的大小限制?