| /* |
| * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| /* |
| * @test |
| * @bug 4530538 |
| * @summary Basic unit test of the synchronization statistics support: |
| * @author Mandy Chung |
| * @author Jaroslav Bachorik |
| * |
| * @run main/othervm SynchronizationStatistics |
| */ |
| |
| import java.lang.management.*; |
| import java.util.concurrent.Phaser; |
| import java.util.function.Supplier; |
| |
| public class SynchronizationStatistics { |
| private static class LockerThread extends Thread { |
| public LockerThread(Runnable r) { |
| super(r, "LockerThread"); |
| } |
| } |
| |
| private static final ThreadMXBean mbean = ManagementFactory.getThreadMXBean(); |
| |
| private static final boolean blockedTimeCheck = |
| mbean.isThreadContentionMonitoringSupported(); |
| |
| |
| public static void main(String args[]) throws Exception { |
| if (blockedTimeCheck) { |
| mbean.setThreadContentionMonitoringEnabled(true); |
| } |
| |
| if (!mbean.isThreadContentionMonitoringEnabled()) { |
| throw new RuntimeException("TEST FAILED: " + |
| "Thread Contention Monitoring is not enabled"); |
| } |
| |
| testBlockingOnSimpleMonitor(); |
| testBlockingOnNestedMonitor(); |
| testWaitingOnSimpleMonitor(); |
| testMultiWaitingOnSimpleMonitor(); |
| testWaitingOnNestedMonitor(); |
| |
| System.out.println("Test passed."); |
| } |
| |
| private static LockerThread newLockerThread(Runnable r) { |
| LockerThread t = new LockerThread(r); |
| t.setDaemon(true); |
| return t; |
| } |
| |
| private static void waitForThreadState(Thread t, Thread.State state) throws InterruptedException { |
| while (t.getState() != state) { |
| Thread.sleep(3); |
| } |
| } |
| |
| /** |
| * Tests that blocking on a single monitor properly increases the |
| * blocked count at least by 1. Also asserts that the correct lock name is provided. |
| */ |
| private static void testBlockingOnSimpleMonitor() throws Exception { |
| System.out.println("testBlockingOnSimpleMonitor"); |
| final Object lock1 = new Object(); |
| System.out.println("Lock1 = " + lock1); |
| |
| final Phaser p = new Phaser(2); |
| LockerThread lt = newLockerThread(new Runnable() { |
| @Override |
| public void run() { |
| p.arriveAndAwaitAdvance(); // phase[1] |
| synchronized(lock1) { |
| System.out.println("[LockerThread obtained Lock1]"); |
| p.arriveAndAwaitAdvance(); // phase[2] |
| } |
| p.arriveAndAwaitAdvance(); // phase[3] |
| } |
| }); |
| |
| lt.start(); |
| long tid = lt.getId(); |
| ThreadInfo ti = mbean.getThreadInfo(tid); |
| String lockName = null; |
| synchronized(lock1) { |
| p.arriveAndAwaitAdvance(); // phase[1] |
| waitForThreadState(lt, Thread.State.BLOCKED); |
| do { |
| lockName = mbean.getThreadInfo(tid).getLockName(); |
| } while (lockName == null); |
| } |
| |
| p.arriveAndAwaitAdvance(); // phase[2] |
| testBlocked(ti, () -> mbean.getThreadInfo(tid), lockName, lock1); |
| p.arriveAndDeregister(); // phase[3] |
| |
| lt.join(); |
| |
| printok(); |
| } |
| |
| /** |
| * Tests that blocking on a nested monitor properly increases the |
| * blocked count at least by 1 - it is not affected by the nesting depth. |
| * Also asserts that the correct lock name is provided. |
| */ |
| private static void testBlockingOnNestedMonitor() throws Exception { |
| System.out.println("testBlockingOnNestedMonitor"); |
| final Object lock1 = new Object(); |
| final Object lock2 = new Object(); |
| |
| System.out.println("Lock1 = " + lock1); |
| System.out.println("Lock2 = " + lock2); |
| |
| final Phaser p = new Phaser(2); |
| LockerThread lt = newLockerThread(new Runnable() { |
| @Override |
| public void run() { |
| p.arriveAndAwaitAdvance(); // phase[1] |
| synchronized(lock1) { |
| System.out.println("[LockerThread obtained Lock1]"); |
| p.arriveAndAwaitAdvance(); // phase[2] |
| p.arriveAndAwaitAdvance(); // phase[3] |
| synchronized(lock2) { |
| System.out.println("[LockerThread obtained Lock2]"); |
| p.arriveAndAwaitAdvance(); // phase[4] |
| } |
| p.arriveAndAwaitAdvance(); // phase[5] |
| } |
| } |
| }); |
| |
| lt.start(); |
| long tid = lt.getId(); |
| ThreadInfo ti = mbean.getThreadInfo(tid); |
| String lockName = null; |
| synchronized(lock1) { |
| p.arriveAndAwaitAdvance(); // phase[1] |
| waitForThreadState(lt, Thread.State.BLOCKED); |
| do { |
| lockName = mbean.getThreadInfo(tid).getLockName(); |
| } while (lockName == null); |
| } |
| p.arriveAndAwaitAdvance(); // phase[2] |
| |
| ti = testBlocked(ti, () -> mbean.getThreadInfo(tid), lockName, lock1); |
| |
| synchronized(lock2) { |
| p.arriveAndAwaitAdvance(); // phase [3] |
| waitForThreadState(lt, Thread.State.BLOCKED); |
| do { |
| lockName = mbean.getThreadInfo(tid).getLockName(); |
| } while (lockName == null); |
| } |
| p.arriveAndAwaitAdvance(); // phase [4] |
| testBlocked(ti, () -> mbean.getThreadInfo(tid), lockName, lock2); |
| p.arriveAndDeregister(); |
| |
| lt.join(); |
| |
| printok(); |
| } |
| |
| /** |
| * Tests that waiting on a single monitor properly increases the waited |
| * count by 1 and the waited time by a positive number. |
| */ |
| private static void testWaitingOnSimpleMonitor() throws Exception { |
| System.out.println("testWaitingOnSimpleMonitor"); |
| final Object lock1 = new Object(); |
| final Phaser p = new Phaser(2); |
| LockerThread lt = newLockerThread(new Runnable() { |
| @Override |
| public void run() { |
| p.arriveAndAwaitAdvance(); // phase[1] |
| synchronized(lock1) { |
| System.out.println("[LockerThread obtained Lock1]"); |
| try { |
| lock1.wait(300); |
| } catch (InterruptedException ex) { |
| // ignore |
| } |
| p.arriveAndAwaitAdvance(); // phase[2] |
| } |
| p.arriveAndAwaitAdvance(); // phase[3] |
| } |
| }); |
| |
| lt.start(); |
| ThreadInfo ti1 = mbean.getThreadInfo(lt.getId()); |
| synchronized(lock1) { |
| p.arriveAndAwaitAdvance(); // phase[1] |
| waitForThreadState(lt, Thread.State.BLOCKED); |
| } |
| p.arriveAndAwaitAdvance(); // phase[2] |
| |
| testWaited(ti1, () -> mbean.getThreadInfo(lt.getId()), 1); |
| p.arriveAndDeregister(); // phase[3] |
| |
| lt.join(); |
| |
| printok(); |
| } |
| |
| /** |
| * Tests that waiting multiple times on the same monitor subsequently |
| * increases the waited count by the number of subsequent calls and the |
| * waited time by a positive number. |
| */ |
| private static void testMultiWaitingOnSimpleMonitor() throws Exception { |
| System.out.println("testWaitingOnMultipleMonitors"); |
| final Object lock1 = new Object(); |
| |
| final Phaser p = new Phaser(2); |
| LockerThread lt = newLockerThread(new Runnable() { |
| @Override |
| public void run() { |
| p.arriveAndAwaitAdvance(); // phase[1] |
| synchronized(lock1) { |
| System.out.println("[LockerThread obtained Lock1]"); |
| for (int i = 0; i < 3; i++) { |
| try { |
| lock1.wait(300); |
| } catch (InterruptedException ex) { |
| // ignore |
| } |
| p.arriveAndAwaitAdvance(); // phase[2-4] |
| } |
| } |
| p.arriveAndAwaitAdvance(); // phase[5] |
| } |
| }); |
| |
| lt.start(); |
| ThreadInfo ti1 = mbean.getThreadInfo(lt.getId()); |
| synchronized(lock1) { |
| p.arriveAndAwaitAdvance(); //phase[1] |
| waitForThreadState(lt, Thread.State.BLOCKED); |
| } |
| int phase = p.getPhase(); |
| while ((p.arriveAndAwaitAdvance() - phase) < 3); // phase[2-4] |
| |
| testWaited(ti1, () -> mbean.getThreadInfo(lt.getId()), 3); |
| p.arriveAndDeregister(); // phase[5] |
| |
| lt.join(); |
| |
| printok(); |
| } |
| |
| /** |
| * Tests that waiting on monitors places in nested synchronized blocks |
| * properly increases the waited count by the number of times the "lock.wait()" |
| * was invoked and the waited time by a positive number. |
| */ |
| private static void testWaitingOnNestedMonitor() throws Exception { |
| System.out.println("testWaitingOnNestedMonitor"); |
| final Object lock1 = new Object(); |
| final Object lock2 = new Object(); |
| final Object lock3 = new Object(); |
| |
| final Phaser p = new Phaser(2); |
| LockerThread lt = newLockerThread(new Runnable() { |
| @Override |
| public void run() { |
| p.arriveAndAwaitAdvance(); // phase[1] |
| synchronized(lock1) { |
| System.out.println("[LockerThread obtained Lock1]"); |
| try { |
| lock1.wait(300); |
| } catch (InterruptedException ex) { |
| // ignore |
| } |
| |
| p.arriveAndAwaitAdvance(); // phase[2] |
| synchronized(lock2) { |
| System.out.println("[LockerThread obtained Lock2]"); |
| try { |
| lock2.wait(300); |
| } catch (InterruptedException ex) { |
| // ignore |
| } |
| |
| p.arriveAndAwaitAdvance(); // phase[3] |
| synchronized(lock3) { |
| System.out.println("[LockerThread obtained Lock3]"); |
| try { |
| lock3.wait(300); |
| } catch (InterruptedException ex) { |
| // ignore |
| } |
| p.arriveAndAwaitAdvance(); // phase[4] |
| } |
| } |
| } |
| p.arriveAndAwaitAdvance(); // phase[5] |
| } |
| }); |
| |
| lt.start(); |
| ThreadInfo ti1 = mbean.getThreadInfo(lt.getId()); |
| synchronized(lock1) { |
| p.arriveAndAwaitAdvance(); // phase[1] |
| waitForThreadState(lt, Thread.State.BLOCKED); |
| } |
| |
| synchronized(lock2) { |
| p.arriveAndAwaitAdvance(); // phase[2] |
| waitForThreadState(lt, Thread.State.BLOCKED); |
| } |
| |
| synchronized(lock3) { |
| p.arriveAndAwaitAdvance(); // phase[3] |
| waitForThreadState(lt, Thread.State.BLOCKED); |
| } |
| |
| p.arriveAndAwaitAdvance(); // phase[4] |
| testWaited(ti1, () -> mbean.getThreadInfo(lt.getId()), 3); |
| p.arriveAndDeregister(); // phase[5] |
| |
| lt.join(); |
| printok(); |
| } |
| |
| private static void printok() { |
| System.out.println("OK\n"); |
| } |
| |
| private static void testWaited(ThreadInfo ti1, Supplier<ThreadInfo> ti2, int waited) |
| throws InterruptedException { |
| boolean error; |
| do { |
| error = false; |
| ThreadInfo ti = ti2.get(); |
| long waitCntDiff = ti.getWaitedCount() - ti1.getWaitedCount(); |
| long waitTimeDiff = ti.getWaitedTime() - ti1.getWaitedTime(); |
| if (waitCntDiff < waited) { |
| System.err.println( |
| "Unexpected diff in waited count. Expecting at least " |
| + waited + " , got " + waitCntDiff |
| ); |
| error = true; |
| } |
| if (waitTimeDiff <= 0) { |
| System.err.println( |
| "Unexpected diff in waited time. Expecting increasing " + |
| "value, got " + waitTimeDiff + "ms" |
| ); |
| error = true; |
| } |
| if (error) { |
| System.err.println("Retrying in 20ms ..."); |
| Thread.sleep(20); |
| } |
| } while (error); |
| } |
| |
| private static ThreadInfo testBlocked(ThreadInfo ti1, Supplier<ThreadInfo> ti2, |
| String lockName, final Object lock) |
| throws InterruptedException { |
| boolean error; |
| ThreadInfo ti = null; |
| do { |
| error = false; |
| ti = ti2.get(); |
| long blkCntDiff = ti.getBlockedCount() - ti1.getBlockedCount(); |
| long blkTimeDiff = ti.getBlockedTime() - ti1.getBlockedTime(); |
| |
| System.out.println("testBlocked: [" + blkCntDiff + ", " + blkTimeDiff + ", " + lockName + "]"); |
| |
| if (blkCntDiff < 1) { |
| System.err.println( |
| "Unexpected diff in blocked count. Expecting at least 1, " + |
| "got " + blkCntDiff |
| ); |
| error = true; |
| } |
| if (blkTimeDiff < 0) { |
| System.err.println( |
| "Unexpected diff in blocked time. Expecting a positive " + |
| "number, got " + blkTimeDiff |
| ); |
| error = true; |
| } |
| if (!lockName.equals(lock.toString())) { |
| System.err.println( |
| "Unexpected blocked monitor name. Expecting " + |
| lock.toString() + ", got " + lockName |
| ); |
| error = true; |
| } |
| if (error) { |
| System.err.println("Retrying in 20ms ..."); |
| Thread.sleep(20); |
| } |
| } while (error); |
| return ti; |
| } |
| } |