import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;

/** Pola produsen-konsumen: antrean berkapasitas sebagai titik serah terima. */
public class SerahTerimaAntrean {

    public static void main(String[] args) throws Exception {
        // Kapasitas 4: produsen menunggu bila antrean penuh (backpressure).
        BlockingQueue<String> antrean = new LinkedBlockingQueue<>(4);
        List<String> paketA = List.of("A1", "A2", "A3", "A4", "A5");
        List<String> paketB = List.of("B1", "B2", "B3", "B4", "B5");
        List<String> paketC = List.of("C1", "C2", "C3", "C4", "C5");

        List<String> terserap = new ArrayList<>(15);
        try (ExecutorService eksekutor = Executors.newVirtualThreadPerTaskExecutor()) {
            // Tiga produsen: satu virtual thread per produsen.
            eksekutor.submit(() -> taruhSemua(antrean, paketA));
            eksekutor.submit(() -> taruhSemua(antrean, paketB));
            eksekutor.submit(() -> taruhSemua(antrean, paketC));
            // Konsumen di main: mengambil tepat 15 paket yang dijanjikan.
            for (int i = 0; i < paketA.size() + paketB.size() + paketC.size(); i++) {
                terserap.add(antrean.take());
            }
        } // close(): menunggu seluruh produsen tuntas sebelum laporan dibuat

        System.out.println("== Antrean serah terima (kapasitas 4) ==");
        System.out.println("produsen        : 3 (virtual thread per produsen)");
        System.out.println("paket diserap   : " + terserap.size());
        System.out.println("sisa di antrean : " + antrean.size());
        terserap.sort(Comparator.naturalOrder());
        System.out.println("urutan serapan (terurut): " + terserap);
    }

    private static void taruhSemua(BlockingQueue<String> antrean, List<String> paket) {
        try {
            for (String p : paket) {
                antrean.put(p); // menunggu bila antrean penuh
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt(); // pulihkan status interupsi
        }
    }
}
