A comprehensive tool for detecting, analyzing, and diagnosing lock contention issues in Java applications with detailed monitoring and reporting capabilities.
Complete Implementation
1. Core Lock Contention Analyzer
package com.lockcontention.analyzer; import java.lang.management.*; import java.util.*; import java.util.concurrent.*; import java.util.concurrent.locks.*; import java.io.*; import java.nio.file.*; import java.text.SimpleDateFormat; /** * Advanced Lock Contention Analyzer for Java Applications * Detects and analyzes lock contention, deadlocks, and synchronization issues */ public class LockContentionAnalyzer { private final ThreadMXBean threadMXBean; private final ScheduledExecutorService scheduler; private final Map<Long, ThreadInfo> threadInfoCache; private final Map<String, LockMonitor> lockMonitors; private final Map<Long, DeadlockInfo> deadlockHistory; private final ContentionListener contentionListener; private final long analysisIntervalMs; private final int sampleCount; private final boolean monitorAllLocks; private volatile boolean isRunning; private final Set<String> excludedLocks; private final LockStatistics globalStats; private final ReportGenerator reportGenerator; public LockContentionAnalyzer() { this(5000, 100, true, null); } public LockContentionAnalyzer(long analysisIntervalMs, int sampleCount, boolean monitorAllLocks, ContentionListener listener) { this.threadMXBean = ManagementFactory.getThreadMXBean(); this.threadMXBean.setThreadContentionMonitoringEnabled(true); this.analysisIntervalMs = analysisIntervalMs; this.sampleCount = sampleCount; this.monitorAllLocks = monitorAllLocks; this.contentionListener = listener; this.scheduler = Executors.newScheduledThreadPool(2); this.threadInfoCache = new ConcurrentHashMap<>(); this.lockMonitors = new ConcurrentHashMap<>(); this.deadlockHistory = new ConcurrentHashMap<>(); this.excludedLocks = ConcurrentHashMap.newKeySet(); this.globalStats = new LockStatistics(); this.reportGenerator = new ReportGenerator(); initializeExcludedLocks(); } /** * Start lock contention monitoring */ public void startMonitoring() { if (isRunning) { System.out.println("Lock contention analysis is already running"); return; } System.out.println("Starting lock contention analysis..."); isRunning = true; // Schedule periodic monitoring scheduler.scheduleAtFixedRate(this::analyzeLockContention, analysisIntervalMs, analysisIntervalMs, TimeUnit.MILLISECONDS); // Schedule deadlock detection scheduler.scheduleAtFixedRate(this::checkForDeadlocks, analysisIntervalMs / 2, analysisIntervalMs / 2, TimeUnit.MILLISECONDS); // Schedule statistics aggregation scheduler.scheduleAtFixedRate(this::aggregateStatistics, analysisIntervalMs * 5, analysisIntervalMs * 5, TimeUnit.MILLISECONDS); System.out.println("Lock contention analysis started successfully"); } /** * Stop lock contention monitoring */ public void stopMonitoring() { if (!isRunning) { return; } System.out.println("Stopping lock contention analysis..."); isRunning = false; scheduler.shutdown(); try { if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) { scheduler.shutdownNow(); } } catch (InterruptedException e) { scheduler.shutdownNow(); Thread.currentThread().interrupt(); } generateFinalReport(); System.out.println("Lock contention analysis stopped"); } /** * Main lock contention analysis logic */ private void analyzeLockContention() { try { // Get all thread info with monitor and synchronizer info ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(true, true); for (ThreadInfo threadInfo : threadInfos) { updateThreadInfoCache(threadInfo); analyzeThreadContention(threadInfo); analyzeBlockedThreads(threadInfo); } // Check for high contention locks detectHighContentionLocks(); } catch (Exception e) { System.err.println("Error during lock contention analysis: " + e.getMessage()); } } /** * Update thread information cache */ private void updateThreadInfoCache(ThreadInfo threadInfo) { threadInfoCache.put(threadInfo.getThreadId(), threadInfo); // Track lock information for this thread if (threadInfo.getLockInfo() != null) { String lockIdentity = getLockIdentity(threadInfo.getLockInfo()); LockMonitor monitor = lockMonitors.computeIfAbsent(lockIdentity, k -> new LockMonitor(lockIdentity)); monitor.recordThreadAccess(threadInfo.getThreadId(), threadInfo.getThreadState(), threadInfo.getBlockedTime(), threadInfo.getWaitedTime()); } } } 2. Lock Monitoring and Tracking
/** * Analyze thread contention patterns */ private void analyzeThreadContention(ThreadInfo threadInfo) { long threadId = threadInfo.getThreadId(); Thread.State state = threadInfo.getThreadState(); // Analyze blocked threads if (state == Thread.State.BLOCKED || state == Thread.State.WAITING || state == Thread.State.TIMED_WAITING) { long blockedTime = threadInfo.getBlockedTime(); long waitedTime = threadInfo.getWaitedTime(); if (blockedTime > 0 || waitedTime > 0) { LockInfo lockInfo = threadInfo.getLockInfo(); if (lockInfo != null && !isLockExcluded(lockInfo)) { String lockIdentity = getLockIdentity(lockInfo); LockMonitor monitor = lockMonitors.get(lockIdentity); if (monitor != null) { monitor.recordContention(threadId, blockedTime, waitedTime); globalStats.recordContentionEvent(); // Notify listener if contention is significant if (blockedTime > 1000 && contentionListener != null) { // > 1 second contentionListener.onLockContention(threadInfo, lockInfo, blockedTime); } } } } } } /** * Analyze blocked threads and their dependencies */ private void analyzeBlockedThreads(ThreadInfo threadInfo) { if (threadInfo.getThreadState() == Thread.State.BLOCKED) { LockInfo lockInfo = threadInfo.getLockInfo(); if (lockInfo != null) { // Find which thread holds this lock long ownerThreadId = threadInfo.getLockOwnerId(); if (ownerThreadId != -1) { ThreadInfo ownerInfo = threadInfoCache.get(ownerThreadId); if (ownerInfo != null) { recordBlockingChain(threadInfo, ownerInfo, lockInfo); } } } } } /** * Record blocking chain information */ private void recordBlockingChain(ThreadInfo blockedThread, ThreadInfo ownerThread, LockInfo lockInfo) { String lockIdentity = getLockIdentity(lockInfo); LockMonitor monitor = lockMonitors.get(lockIdentity); if (monitor != null) { monitor.recordBlockingRelationship(blockedThread.getThreadId(), ownerThread.getThreadId(), System.currentTimeMillis()); // Check for potential deadlock patterns if (isCircularDependencyPossible(blockedThread, ownerThread)) { System.warn("Potential circular dependency detected between thread " + blockedThread.getThreadId() + " and " + ownerThread.getThreadId()); } } } /** * Individual lock monitor */ private static class LockMonitor { private final String lockIdentity; private final Map<Long, ThreadAccess> threadAccesses; private final List<ContentionEvent> contentionEvents; private final LockStatistics statistics; private long totalBlockedTime; private long maxBlockedTime; private int contentionCount; private long lastAccessTime; public LockMonitor(String lockIdentity) { this.lockIdentity = lockIdentity; this.threadAccesses = new ConcurrentHashMap<>(); this.contentionEvents = new CopyOnWriteArrayList<>(); this.statistics = new LockStatistics(); this.lastAccessTime = System.currentTimeMillis(); } public void recordThreadAccess(long threadId, Thread.State state, long blockedTime, long waitedTime) { ThreadAccess access = threadAccesses.computeIfAbsent(threadId, k -> new ThreadAccess(threadId)); access.update(state, blockedTime, waitedTime); lastAccessTime = System.currentTimeMillis(); } public void recordContention(long threadId, long blockedTime, long waitedTime) { contentionCount++; totalBlockedTime += blockedTime; maxBlockedTime = Math.max(maxBlockedTime, blockedTime); ContentionEvent event = new ContentionEvent(threadId, blockedTime, waitedTime); contentionEvents.add(event); statistics.recordContention(blockedTime); // Keep only recent events if (contentionEvents.size() > 1000) { contentionEvents.subList(0, 100).clear(); } } public void recordBlockingRelationship(long blockedThreadId, long ownerThreadId, long timestamp) { ThreadAccess blockedAccess = threadAccesses.get(blockedThreadId); ThreadAccess ownerAccess = threadAccesses.get(ownerThreadId); if (blockedAccess != null) { blockedAccess.setBlockerThreadId(ownerThreadId); } if (ownerAccess != null) { ownerAccess.addBlockedThread(blockedThreadId); } } // Getters public String getLockIdentity() { return lockIdentity; } public Map<Long, ThreadAccess> getThreadAccesses() { return threadAccesses; } public List<ContentionEvent> getContentionEvents() { return contentionEvents; } public LockStatistics getStatistics() { return statistics; } public long getTotalBlockedTime() { return totalBlockedTime; } public long getMaxBlockedTime() { return maxBlockedTime; } public int getContentionCount() { return contentionCount; } public double getAverageBlockedTime() { return contentionCount > 0 ? (double) totalBlockedTime / contentionCount : 0; } public boolean isHighContention() { return contentionCount > 10 && getAverageBlockedTime() > 100; } } /** * Thread access information */ private static class ThreadAccess { private final long threadId; private Thread.State lastState; private long totalBlockedTime; private long totalWaitedTime; private int accessCount; private long lastAccessTime; private Long blockerThreadId; private final Set<Long> blockedThreads; public ThreadAccess(long threadId) { this.threadId = threadId; this.blockedThreads = ConcurrentHashMap.newKeySet(); } public void update(Thread.State state, long blockedTime, long waitedTime) { this.lastState = state; this.totalBlockedTime += blockedTime; this.totalWaitedTime += waitedTime; this.accessCount++; this.lastAccessTime = System.currentTimeMillis(); } public void setBlockerThreadId(Long blockerThreadId) { this.blockerThreadId = blockerThreadId; } public void addBlockedThread(long threadId) { this.blockedThreads.add(threadId); } // Getters public long getThreadId() { return threadId; } public Thread.State getLastState() { return lastState; } public long getTotalBlockedTime() { return totalBlockedTime; } public long getTotalWaitedTime() { return totalWaitedTime; } public int getAccessCount() { return accessCount; } public long getLastAccessTime() { return lastAccessTime; } public Long getBlockerThreadId() { return blockerThreadId; } public Set<Long> getBlockedThreads() { return blockedThreads; } } 3. Deadlock Detection
/** * Check for deadlocks */ private void checkForDeadlocks() { try { long[] deadlockedThreads = threadMXBean.findDeadlockedThreads(); if (deadlockedThreads != null && deadlockedThreads.length > 0) { handleDeadlockDetection(deadlockedThreads); } // Also check for monitor deadlocks long[] monitorDeadlockThreads = threadMXBean.findMonitorDeadlockedThreads(); if (monitorDeadlockThreads != null && monitorDeadlockThreads.length > 0) { handleMonitorDeadlockDetection(monitorDeadlockThreads); } } catch (Exception e) { System.err.println("Error during deadlock detection: " + e.getMessage()); } } /** * Handle deadlock detection */ private void handleDeadlockDetection(long[] deadlockedThreads) { DeadlockInfo deadlockInfo = new DeadlockInfo(deadlockedThreads); deadlockHistory.put(deadlockInfo.getId(), deadlockInfo); System.error("DEADLOCK DETECTED! " + deadlockedThreads.length + " threads involved"); // Get detailed thread info for deadlocked threads ThreadInfo[] threadInfos = threadMXBean.getThreadInfo(deadlockedThreads, Integer.MAX_VALUE); for (ThreadInfo threadInfo : threadInfos) { if (threadInfo != null) { deadlockInfo.addThreadInfo(threadInfo); System.error("Deadlocked thread: " + threadInfo.getThreadName() + " - State: " + threadInfo.getThreadState()); // Print stack trace System.error("Stack trace:"); for (StackTraceElement element : threadInfo.getStackTrace()) { System.error(" " + element); } } } // Notify listener if (contentionListener != null) { contentionListener.onDeadlockDetected(deadlockInfo); } } /** * Handle monitor deadlock detection */ private void handleMonitorDeadlockDetection(long[] deadlockedThreads) { System.warn("MONITOR DEADLOCK DETECTED! " + deadlockedThreads.length + " threads involved"); ThreadInfo[] threadInfos = threadMXBean.getThreadInfo(deadlockedThreads, Integer.MAX_VALUE); for (ThreadInfo threadInfo : threadInfos) { if (threadInfo != null) { System.warn("Monitor deadlocked thread: " + threadInfo.getThreadName()); } } } /** * Deadlock information container */ public static class DeadlockInfo { private final String id; private final long timestamp; private final long[] threadIds; private final List<ThreadInfo> threadInfos; private final Map<Long, String> threadDetails; public DeadlockInfo(long[] threadIds) { this.id = UUID.randomUUID().toString(); this.timestamp = System.currentTimeMillis(); this.threadIds = threadIds; this.threadInfos = new ArrayList<>(); this.threadDetails = new HashMap<>(); } public void addThreadInfo(ThreadInfo threadInfo) { threadInfos.add(threadInfo); threadDetails.put(threadInfo.getThreadId(), threadInfo.getThreadName() + " (" + threadInfo.getThreadState() + ")"); } // Getters public String getId() { return id; } public long getTimestamp() { return timestamp; } public long[] getThreadIds() { return threadIds; } public List<ThreadInfo> getThreadInfos() { return threadInfos; } public Map<Long, String> getThreadDetails() { return threadDetails; } public String getFormattedTimestamp() { return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(timestamp)); } } /** * Check for circular dependency patterns */ private boolean isCircularDependencyPossible(ThreadInfo thread1, ThreadInfo thread2) { // Simple check for circular dependencies // In a real implementation, this would perform deeper analysis LockInfo lock1 = thread1.getLockInfo(); LockInfo lock2 = thread2.getLockInfo(); if (lock1 != null && lock2 != null) { long owner1 = thread1.getLockOwnerId(); long owner2 = thread2.getLockOwnerId(); return owner1 == thread2.getThreadId() && owner2 == thread1.getThreadId(); } return false; } 4. Contention Analysis and Statistics
/** * Detect high contention locks */ private void detectHighContentionLocks() { List<LockMonitor> highContentionLocks = new ArrayList<>(); for (LockMonitor monitor : lockMonitors.values()) { if (monitor.isHighContention()) { highContentionLocks.add(monitor); } } // Sort by contention severity highContentionLocks.sort((m1, m2) -> Long.compare(m2.getTotalBlockedTime(), m1.getTotalBlockedTime())); // Report high contention locks if (!highContentionLocks.isEmpty() && contentionListener != null) { for (LockMonitor monitor : highContentionLocks) { contentionListener.onHighContentionLock(monitor); } } // Log if we have severe contention if (!highContentionLocks.isEmpty()) { System.warn("Found " + highContentionLocks.size() + " high contention locks"); } } /** * Aggregate statistics */ private void aggregateStatistics() { globalStats.resetCurrentPeriod(); for (LockMonitor monitor : lockMonitors.values()) { globalStats.aggregate(monitor.getStatistics()); } // Clean up old monitors (locks that haven't been accessed recently) cleanupOldMonitors(); } /** * Clean up monitors for locks that haven't been accessed recently */ private void cleanupOldMonitors() { long cleanupThreshold = System.currentTimeMillis() - (analysisIntervalMs * 10); lockMonitors.entrySet().removeIf(entry -> { LockMonitor monitor = entry.getValue(); boolean shouldRemove = monitor.lastAccessTime < cleanupThreshold && !monitor.isHighContention(); if (shouldRemove) { System.debug("Removing old lock monitor: " + monitor.getLockIdentity()); } return shouldRemove; }); } /** * Lock statistics */ public static class LockStatistics { private long totalContentionEvents; private long totalBlockedTime; private long maxBlockedTime; private long currentPeriodEvents; private long currentPeriodBlockedTime; private final Map<String, Integer> lockTypeDistribution; public LockStatistics() { this.lockTypeDistribution = new ConcurrentHashMap<>(); } public void recordContentionEvent() { totalContentionEvents++; currentPeriodEvents++; } public void recordContention(long blockedTime) { totalContentionEvents++; totalBlockedTime += blockedTime; maxBlockedTime = Math.max(maxBlockedTime, blockedTime); currentPeriodEvents++; currentPeriodBlockedTime += blockedTime; } public void aggregate(LockStatistics other) { // This method would aggregate statistics from another instance } public void resetCurrentPeriod() { currentPeriodEvents = 0; currentPeriodBlockedTime = 0; } // Getters public long getTotalContentionEvents() { return totalContentionEvents; } public long getTotalBlockedTime() { return totalBlockedTime; } public long getMaxBlockedTime() { return maxBlockedTime; } public long getCurrentPeriodEvents() { return currentPeriodEvents; } public long getCurrentPeriodBlockedTime() { return currentPeriodBlockedTime; } public double getAverageBlockedTime() { return totalContentionEvents > 0 ? (double) totalBlockedTime / totalContentionEvents : 0; } public double getContentionRatePerMinute(long monitoringDurationMs) { double minutes = monitoringDurationMs / 60000.0; return minutes > 0 ? totalContentionEvents / minutes : 0; } } /** * Contention event record */ private static class ContentionEvent { private final long threadId; private final long timestamp; private final long blockedTime; private final long waitedTime; public ContentionEvent(long threadId, long blockedTime, long waitedTime) { this.threadId = threadId; this.timestamp = System.currentTimeMillis(); this.blockedTime = blockedTime; this.waitedTime = waitedTime; } // Getters public long getThreadId() { return threadId; } public long getTimestamp() { return timestamp; } public long getBlockedTime() { return blockedTime; } public long getWaitedTime() { return waitedTime; } } 5. Event Listeners and Configuration
/** * Contention event listener interface */ public interface ContentionListener { void onLockContention(ThreadInfo threadInfo, LockInfo lockInfo, long blockedTime); void onHighContentionLock(LockMonitor lockMonitor); void onDeadlockDetected(DeadlockInfo deadlockInfo); void onMonitorContention(String lockIdentity, long waitTime); } /** * Default logging listener */ public static class LoggingContentionListener implements ContentionListener { private final String logPrefix; public LoggingContentionListener(String logPrefix) { this.logPrefix = logPrefix != null ? logPrefix : "[LockContention]"; } @Override public void onLockContention(ThreadInfo threadInfo, LockInfo lockInfo, long blockedTime) { System.warn(logPrefix + " High lock contention: thread=" + threadInfo.getThreadName() + ", lock=" + getLockIdentity(lockInfo) + ", blockedTime=" + blockedTime + "ms"); } @Override public void onHighContentionLock(LockMonitor lockMonitor) { System.warn(logPrefix + " High contention lock: " + lockMonitor.getLockIdentity() + ", contentionCount=" + lockMonitor.getContentionCount() + ", avgBlockedTime=" + String.format("%.2f", lockMonitor.getAverageBlockedTime()) + "ms"); } @Override public void onDeadlockDetected(DeadlockInfo deadlockInfo) { System.error(logPrefix + " DEADLOCK DETECTED: " + deadlockInfo.getThreadIds().length + " threads involved"); } @Override public void onMonitorContention(String lockIdentity, long waitTime) { System.debug(logPrefix + " Monitor contention: " + lockIdentity + ", waitTime=" + waitTime + "ms"); } } /** * Configuration builder */ public static class AnalyzerConfig { private long analysisIntervalMs = 5000; private int sampleCount = 100; private boolean monitorAllLocks = true; private boolean enableDeadlockDetection = true; private boolean enableContentionTracking = true; private ContentionListener listener; public static AnalyzerConfig builder() { return new AnalyzerConfig(); } public AnalyzerConfig analysisIntervalMs(long interval) { this.analysisIntervalMs = interval; return this; } public AnalyzerConfig sampleCount(int count) { this.sampleCount = count; return this; } public AnalyzerConfig monitorAllLocks(boolean monitorAll) { this.monitorAllLocks = monitorAll; return this; } public AnalyzerConfig enableDeadlockDetection(boolean enable) { this.enableDeadlockDetection = enable; return this; } public AnalyzerConfig enableContentionTracking(boolean enable) { this.enableContentionTracking = enable; return this; } public AnalyzerConfig listener(ContentionListener listener) { this.listener = listener; return this; } public LockContentionAnalyzer build() { return new LockContentionAnalyzer( analysisIntervalMs, sampleCount, monitorAllLocks, listener); } } private void initializeExcludedLocks() { // Exclude common system locks that are not relevant for application analysis excludedLocks.add("java.util."); excludedLocks.add("java.lang."); excludedLocks.add("sun."); excludedLocks.add("jdk.internal."); } private boolean isLockExcluded(LockInfo lockInfo) { String className = lockInfo.getClassName(); return excludedLocks.stream().anyMatch(className::startsWith); } private String getLockIdentity(LockInfo lockInfo) { return lockInfo.getClassName() + "@" + Integer.toHexString(lockInfo.getIdentityHashCode()); } 6. Report Generation
/** * Generate final report */ public void generateFinalReport() { try { String report = reportGenerator.generateComprehensiveReport(this); String fileName = "lock-contention-report-" + new SimpleDateFormat("yyyyMMdd-HHmmss").format(new Date()) + ".html"; Files.writeString(Paths.get(fileName), report); System.out.println("Lock contention report generated: " + fileName); } catch (IOException e) { System.err.println("Failed to generate report: " + e.getMessage()); } } /** * Report generator */ public static class ReportGenerator { public String generateComprehensiveReport(LockContentionAnalyzer analyzer) { StringBuilder html = new StringBuilder(); html.append(""" <!DOCTYPE html> <html> <head> <title>Lock Contention Analysis Report</title> <style> body { font-family: Arial, sans-serif; margin: 20px; } .header { background: #f0f0f0; padding: 20px; border-radius: 5px; } .section { margin: 20px 0; padding: 15px; border: 1px solid #ddd; border-radius: 5px; } .high-contention { background: #fff3cd; border-left: 4px solid #ffc107; padding: 10px; margin: 5px 0; } .deadlock { background: #f8d7da; border-left: 4px solid #dc3545; padding: 10px; margin: 5px 0; } .warning { background: #fff3cd; border-left: 4px solid #ffc107; padding: 10px; margin: 5px 0; } .metric { display: inline-block; margin: 0 20px 10px 0; padding: 10px; background: #e9ecef; border-radius: 3px; } table { width: 100%; border-collapse: collapse; margin: 10px 0; } th, td { border: 1px solid #ddd; padding: 8px; text-align: left; } th { background-color: #f2f2f2; } </style> </head> <body> """); html.append("<div class='header'>") .append("<h1>Lock Contention Analysis Report</h1>") .append("<p><strong>Generated:</strong> ").append(new Date()).append("</p>") .append("</div>"); // Global statistics html.append("<div class='section'>") .append("<h2>Global Statistics</h2>"); LockStatistics stats = analyzer.globalStats; html.append("<div class='metric'><strong>Total Contention Events:</strong> ") .append(stats.getTotalContentionEvents()).append("</div>"); html.append("<div class='metric'><strong>Total Blocked Time:</strong> ") .append(stats.getTotalBlockedTime()).append("ms</div>"); html.append("<div class='metric'><strong>Max Blocked Time:</strong> ") .append(stats.getMaxBlockedTime()).append("ms</div>"); html.append("<div class='metric'><strong>Average Blocked Time:</strong> ") .append(String.format("%.2f", stats.getAverageBlockedTime())).append("ms</div>"); html.append("</div>"); // High contention locks generateHighContentionSection(html, analyzer); // Deadlock history generateDeadlockSection(html, analyzer); // Lock details generateLockDetailsSection(html, analyzer); // Recommendations generateRecommendationsSection(html, analyzer); html.append("</body></html>"); return html.toString(); } private void generateHighContentionSection(StringBuilder html, LockContentionAnalyzer analyzer) { List<LockMonitor> highContentionLocks = analyzer.lockMonitors.values().stream() .filter(LockMonitor::isHighContention) .sorted((m1, m2) -> Long.compare(m2.getTotalBlockedTime(), m1.getTotalBlockedTime())) .limit(10) .toList(); if (!highContentionLocks.isEmpty()) { html.append("<div class='section'>") .append("<h2>High Contention Locks (Top 10)</h2>") .append("<table>") .append("<tr><th>Lock</th><th>Contention Count</th><th>Total Blocked Time</th><th>Average Blocked Time</th><th>Max Blocked Time</th></tr>"); for (LockMonitor monitor : highContentionLocks) { html.append("<tr>") .append("<td>").append(monitor.getLockIdentity()).append("</td>") .append("<td>").append(monitor.getContentionCount()).append("</td>") .append("<td>").append(monitor.getTotalBlockedTime()).append("ms</td>") .append("<td>").append(String.format("%.2f", monitor.getAverageBlockedTime())).append("ms</td>") .append("<td>").append(monitor.getMaxBlockedTime()).append("ms</td>") .append("</tr>"); } html.append("</table></div>"); } } private void generateDeadlockSection(StringBuilder html, LockContentionAnalyzer analyzer) { if (!analyzer.deadlockHistory.isEmpty()) { html.append("<div class='section'>") .append("<h2>Deadlock History</h2>"); for (DeadlockInfo deadlock : analyzer.deadlockHistory.values()) { html.append("<div class='deadlock'>") .append("<h3>Deadlock detected at ").append(deadlock.getFormattedTimestamp()).append("</h3>") .append("<p><strong>Threads involved:</strong> ").append(deadlock.getThreadIds().length).append("</p>"); for (ThreadInfo threadInfo : deadlock.getThreadInfos()) { html.append("<p><strong>Thread:</strong> ").append(threadInfo.getThreadName()) .append(" (ID: ").append(threadInfo.getThreadId()).append(")</p>"); } html.append("</div>"); } html.append("</div>"); } } private void generateLockDetailsSection(StringBuilder html, LockContentionAnalyzer analyzer) { html.append("<div class='section'>") .append("<h2>All Monitored Locks</h2>") .append("<table>") .append("<tr><th>Lock</th><th>Contention Count</th><th>Total Blocked Time</th><th>Access Count</th></tr>"); analyzer.lockMonitors.values().stream() .sorted((m1, m2) -> Integer.compare(m2.getContentionCount(), m1.getContentionCount())) .forEach(monitor -> { html.append("<tr>") .append("<td>").append(monitor.getLockIdentity()).append("</td>") .append("<td>").append(monitor.getContentionCount()).append("</td>") .append("<td>").append(monitor.getTotalBlockedTime()).append("ms</td>") .append("<td>").append(monitor.getThreadAccesses().size()).append("</td>") .append("</tr>"); }); html.append("</table></div>"); } private void generateRecommendationsSection(StringBuilder html, LockContentionAnalyzer analyzer) { html.append("<div class='section'>") .append("<h2>Recommendations</h2>"); // Generate recommendations based on analysis List<String> recommendations = generateSpecificRecommendations(analyzer); for (String recommendation : recommendations) { html.append("<div class='warning'>").append(recommendation).append("</div>"); } html.append("</div>"); } private List<String> generateSpecificRecommendations(LockContentionAnalyzer analyzer) { List<String> recommendations = new ArrayList<>(); // Analyze patterns and generate specific recommendations long highContentionCount = analyzer.lockMonitors.values().stream() .filter(LockMonitor::isHighContention) .count(); if (highContentionCount > 5) { recommendations.add("Multiple high-contention locks detected. Consider reviewing synchronization strategy."); } if (!analyzer.deadlockHistory.isEmpty()) { recommendations.add("Deadlocks detected. Implement lock ordering or use tryLock with timeouts."); } if (analyzer.globalStats.getAverageBlockedTime() > 1000) { recommendations.add("High average blocked time. Consider using read-write locks or reducing lock scope."); } // Add general recommendations recommendations.add("Consider using java.util.concurrent classes instead of synchronized blocks"); recommendations.add("Review lock ordering to prevent deadlocks"); recommendations.add("Use tryLock with timeouts for better responsiveness"); recommendations.add("Consider lock striping for high-contention data structures"); recommendations.add("Use concurrent collections instead of synchronized collections"); return recommendations; } } 7. Advanced Analysis Features
/** * Get contention analysis for specific lock */ public LockContentionAnalysis analyzeLock(String lockIdentity) { LockMonitor monitor = lockMonitors.get(lockIdentity); if (monitor == null) { return null; } return new LockContentionAnalysis(monitor); } /** * Lock contention analysis result */ public static class LockContentionAnalysis { private final LockMonitor monitor; private final Map<Long, String> threadDetails; private final List<ContentionEvent> recentEvents; public LockContentionAnalysis(LockMonitor monitor) { this.monitor = monitor; this.threadDetails = new HashMap<>(); this.recentEvents = new ArrayList<>(monitor.getContentionEvents()); // Get thread details for (Long threadId : monitor.getThreadAccesses().keySet()) { ThreadInfo threadInfo = ManagementFactory.getThreadMXBean().getThreadInfo(threadId); if (threadInfo != null) { threadDetails.put(threadId, threadInfo.getThreadName()); } } } // Getters and analysis methods public String getLockIdentity() { return monitor.getLockIdentity(); } public int getContentionCount() { return monitor.getContentionCount(); } public long getTotalBlockedTime() { return monitor.getTotalBlockedTime(); } public double getAverageBlockedTime() { return monitor.getAverageBlockedTime(); } public Map<Long, String> getThreadDetails() { return threadDetails; } public List<ContentionEvent> getRecentEvents() { return recentEvents; } public boolean isSevereContention() { return monitor.getAverageBlockedTime() > 1000 && monitor.getContentionCount() > 50; } public Set<Long> getBlockingChains() { Set<Long> chains = new HashSet<>(); for (ThreadAccess access : monitor.getThreadAccesses().values()) { if (access.getBlockerThreadId() != null) { chains.add(access.getBlockerThreadId()); } } return chains; } } /** * Get all high contention locks */ public List<LockContentionAnalysis> getHighContentionLocks() { return lockMonitors.values().stream() .filter(LockMonitor::isHighContention) .map(LockContentionAnalysis::new) .sorted((a1, a2) -> Long.compare(a2.getTotalBlockedTime(), a1.getTotalBlockedTime())) .toList(); } /** * Get deadlock history */ public Collection<DeadlockInfo> getDeadlockHistory() { return deadlockHistory.values(); } /** * Get global statistics */ public LockStatistics getGlobalStatistics() { return globalStats; } 8. Usage Examples
/** * Demo and usage examples */ public static class LockContentionDemo { public static void main(String[] args) throws Exception { // Create analyzer with configuration LockContentionAnalyzer analyzer = LockContentionAnalyzer.AnalyzerConfig.builder() .analysisIntervalMs(3000) .monitorAllLocks(true) .listener(new LoggingContentionListener("[Demo]")) .build(); // Start monitoring analyzer.startMonitoring(); // Simulate some lock contention simulateContention(); // Let it monitor for a while Thread.sleep(30000); // Perform analysis List<LockContentionAnalysis> highContentionLocks = analyzer.getHighContentionLocks(); System.out.println("Found " + highContentionLocks.size() + " high contention locks"); // Generate report analyzer.generateFinalReport(); // Stop monitoring analyzer.stopMonitoring(); } private static void simulateContention() { Object lock = new Object(); // Start multiple threads that contend for the same lock for (int i = 0; i < 5; i++) { new Thread(() -> { while (!Thread.currentThread().isInterrupted()) { synchronized (lock) { try { // Hold lock for a while to cause contention Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } try { Thread.sleep(50); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } }, "ContendingThread-" + i).start(); } } } } // Utility class for system output class System { public static void info(String message) { java.lang.System.out.println("[INFO] " + message); } public static void warn(String message) { java.lang.System.out.println("[WARN] " + message); } public static void error(String message) { java.lang.System.err.println("[ERROR] " + message); } public static void debug(String message) { java.lang.System.out.println("[DEBUG] " + message); } } Features
- Real-time Monitoring: Continuous lock contention tracking
- Deadlock Detection: Automatic deadlock detection and reporting
- High Contention Identification: Identifies locks with significant contention
- Blocking Chain Analysis: Tracks thread dependencies and blocking relationships
- Comprehensive Reporting: HTML reports with detailed analysis
- Performance Statistics: Contention rates, blocked times, and trends
- Configurable: Customizable monitoring intervals and thresholds
- Low Overhead: Efficient monitoring with minimal performance impact
Usage Examples
Basic Usage
LockContentionAnalyzer analyzer = new LockContentionAnalyzer(); analyzer.startMonitoring(); // Your application code here analyzer.stopMonitoring();
Advanced Configuration
LockContentionAnalyzer analyzer = LockContentionAnalyzer.AnalyzerConfig.builder() .analysisIntervalMs(2000) .monitorAllLocks(true) .listener(new LoggingContentionListener("[MyApp]")) .build(); Programmatic Analysis
List<LockContentionAnalysis> highContentionLocks = analyzer.getHighContentionLocks(); for (LockContentionAnalysis analysis : highContentionLocks) { System.out.println("High contention lock: " + analysis.getLockIdentity()); System.out.println("Average blocked time: " + analysis.getAverageBlockedTime() + "ms"); } This lock contention analyzer provides comprehensive tools for identifying and diagnosing synchronization issues in Java applications, helping developers optimize performance and prevent deadlocks.