A simple worker pipeline separates preparing work from consuming work. The example stays deterministic by using an in-memory queue and waiting for both threads to finish.

Producer and Consumer

WorkerPipeline.java
import java.util.ArrayDeque;
import java.util.Queue;

public class WorkerPipeline {
    static class Mailbox {
        private final Queue<String> items = new ArrayDeque<>();

        synchronized void add(String item) {
            items.add(item);
            System.out.println("added=" + item);
        }

        synchronized String take() {
            String item = items.remove();
            System.out.println("took=" + item);
            return item;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        int itemCount = ;
        Mailbox mailbox = new Mailbox();

        Thread producer = new Thread(() -> {
            for (int i = 1; i <= itemCount; i++) {
                mailbox.add("job-" + i);
            }
        });

        producer.start();
        producer.join();

        Thread consumer = new Thread(() -> {
            for (int i = 1; i <= itemCount; i++) {
                String item = mailbox.take();
                System.out.println("processed=" + item);
            }
        });

        consumer.start();
        consumer.join();
    }
}
import java.util.ArrayDeque;
import java.util.Queue;

public class WorkerPipeline {
    static class Mailbox {
        private final Queue<String> items = new ArrayDeque<>();

        synchronized void add(String item) {
            items.add(item);
            System.out.println("added=" + item);
        }

        synchronized String take() {
            String item = items.remove();
            System.out.println("took=" + item);
            return item;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        int itemCount = ;
        Mailbox mailbox = new Mailbox();

        Thread producer = new Thread(() -> {
            for (int i = 1; i <= itemCount; i++) {
                mailbox.add("job-" + i);
            }
        });

        producer.start();
        producer.join();

        Thread consumer = new Thread(() -> {
            for (int i = 1; i <= itemCount; i++) {
                String item = mailbox.take();
                System.out.println("processed=" + item);
            }
        });

        consumer.start();
        consumer.join();
    }
}
queue A queue stores items in the order they should be processed.
producer consumer One thread can produce items while another thread consumes them.