Search code examples
javamultithreadingconcurrencysynchronizationproducer-consumer

Shared item producers and consumer problem


I have a flavor of producer consumer problem (I couldn't find any similar questions and ran out of search keywords) where

  1. Consumers are themselves are producers.
  2. The items produced can be shared.
  3. If some thread is producing, the other threads wait for it to complete and use the same produced item.

I came up with a solution, but I am stuck on how to test it. Is there a theoretical framework to verify the correctness of solutions to these kind of problems. Also is it possible to test these solutions without modifying the source

In case you are interested in the code, it's listed below

import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

public class SharedItemProducer {

    /* testing parameters */
    static final int randMax = 1;
    static final Random R = new Random();
    static final int numThreads = 8;
    static final long produceDelay = 0;
    static final long maxRunTime = 5000;

    Integer item = 0;
    final Object waitingRoom = new Object();
    AtomicInteger wantToBeProducer = new AtomicInteger(0);

    public void produce() {
        log("entering produce");

        if (wantToBeProducer.compareAndSet(0, 1)) {
            log("i'm the producer. cur=%d", item);
            try {
                Thread.sleep(produceDelay);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            item = item + 1;
            synchronized (waitingRoom) {
                waitingRoom.notifyAll();
            }
            wantToBeProducer.set(0);
            log("done producing");
        } else {
            log("someone else is producing, waiting..");
            synchronized (waitingRoom) {
                try {
                    waitingRoom.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            log("wait complete");
        }
    }

    public static void main(String[] args) {

        long start = System.currentTimeMillis();

        /* run the tests */
        SharedItemProducer1 P = new SharedItemProducer1();
        for (int i = 0; i < numThreads; i++) {
            new Thread(() -> {
                while (true) {
                    P.produce();
                    try {
                        Thread.sleep(R.nextInt(randMax));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }

        /* limit time for tests */
        new Thread(() -> {
            try {
                Thread.sleep(maxRunTime);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.exit(0);
        }).start();
    }

    static boolean enableLog = false;
    static final String FMT = "[%s] [%s] ";

    public static void log(String info, Object... params) {
        if (!enableLog)
            return;
        Object[] pfParams = new Object[params.length + 2];
        System.arraycopy(params, 0, pfParams, 2, params.length);
        pfParams[0] = new Date();
        pfParams[1] = Thread.currentThread().getName();
        System.out.printf(FMT + info + "\n", pfParams);
    }
}

Solution

  • If I understood correctly, you are asking for a way to control thread interleaving so that you can verify possible edge cases and weird scenarios.

    I'd recommend you to have a look at Baeldung - Testing Multi-Threaded Code in Java and see if it helps.

    It talks not only about concepts and strategies for testing concurrency code, but also refers to some tools that might be helpful, including:

    Please have a look and give them a try.

    To better test your code, I'd also suggest you modularize it a bit more, so you can test the producer and the consumer in isolation.

    EDIT: To come up with test cases, it might be helpful a list of typical concurrency problems that you may want to verify in your code. Please have a look this blog post from the CMU SEI called Testing Concurrent Systems: Concurrency Defects, Testing Techniques, and Recommendations for an extensive list.