import java.util.concurrent.Semaphore;

public class ThreadStalls {
    static final int THREADS = Integer.getInteger("threads", 1024);
    static final int CONCURRENCY = Integer.getInteger("concurrency", 1024);

    static final Semaphore semaphore = new Semaphore(0);
    static final Object[] sinks = new Object[64 * THREADS];
    static volatile boolean stop;

    public static void main(String... args) throws Throwable {
        for (int t = 0; t < THREADS; t++) {
            int ft = t;
            new Thread(() -> work(ft * 64)).start();
        }

        semaphore.release(CONCURRENCY);

        long deadline = System.nanoTime() + 30_000_000_000L;
        while (System.nanoTime() < deadline) {
          System.out.println("max stall = " + maxStall/1_000_000 + " ms");
          maxStall = 0;
          Thread.sleep(1000);
        }
        stop = true;
    }

    // Not synchronized, but we do not care.
    static long maxStall = 0;

    public static void work(int idx) {
        while (!stop) {
            semaphore.acquireUninterruptibly();
            try {
                long t = System.nanoTime();
                sinks[idx] = new byte[128];
                long stall = (System.nanoTime() - t);
                if (maxStall < stall) maxStall = stall;
            } catch (Throwable ex) {
                throw new RuntimeException(ex);
            } finally {
                semaphore.release();
            }
        }
    }
}
