import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;

public class PetaKonkuren {
    public static void main(String[] args) throws Exception {
        Map<String, Integer> hitungKata = new ConcurrentHashMap<>();
        List<List<String>> dokumen = List.of(
                List.of("kopi", "teh", "kopi"),
                List.of("teh", "susu", "teh"),
                List.of("kopi", "susu", "jus"));

        // Tiga pekerja menghitung kata pada peta yang SAMA tanpa lock manual:
        // merge() menambah nilai secara atomik per entri.
        try (ExecutorService eksekutor = Executors.newFixedThreadPool(3)) {
            for (List<String> kataDokumen : dokumen) {
                eksekutor.submit(() -> {
                    for (String kata : kataDokumen) {
                        hitungKata.merge(kata, 1, Integer::sum);
                    }
                });
            }
        } // try-with-resources: menunggu seluruh task selesai

        System.out.println("jumlah kata (terurut menurut alfabet):");
        for (Map.Entry<String, Integer> e : new TreeMap<>(hitungKata).entrySet()) {
            System.out.println("  " + e.getKey() + ": " + e.getValue());
        }

        // computeIfAbsent pada ConcurrentHashMap bersifat atomik: fungsi
        // pembuat nilai dijamin dijalankan tepat SATU kali meski dikejar
        // banyak thread yang meminta kunci yang sama bersamaan.
        Map<String, Integer> cacheKonfigurasi = new ConcurrentHashMap<>();
        AtomicInteger pemanggilanLoader = new AtomicInteger();
        try (ExecutorService eksekutor = Executors.newFixedThreadPool(3)) {
            for (int i = 0; i < 3; i++) {
                eksekutor.submit(() -> {
                    cacheKonfigurasi.computeIfAbsent("katalog", kunci -> {
                        pemanggilanLoader.incrementAndGet(); // penghitung pemanggilan
                        return 42; // nilai "dimuat dari sumber luar"
                    });
                });
            }
        }
        System.out.println("loader katalog dijalankan: " + pemanggilanLoader.get() + " kali");
        System.out.println("nilai katalog            : " + cacheKonfigurasi.get("katalog"));
    }
}
