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.