Fixing Spring Boot 3 Deadlock: Reproduce, Analyze, and Resolve Custom Event Publishing Issues

This article demonstrates how a custom event published from a constructor in Spring Boot 3.2.5 can cause a deadlock, explains the underlying lock contention, and provides two solutions: using SmartInitializingSingleton to defer publishing and upgrading to Spring 6.2's multithreaded bean initialization.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Fixing Spring Boot 3 Deadlock: Reproduce, Analyze, and Resolve Custom Event Publishing Issues

Environment: Spring Boot 3.2.5.

1. Deadlock Reproduction

1.1 Custom Event Definition

public class PackApplicationEvent extends ApplicationEvent {
    private String message;
    public PackApplicationEvent(String message, Object source) {
        super(source);
        this.message = message;
    }
    public String getMessage() {
        return message;
    }
}

Custom event carries a message and source data.

1.2 Custom Event Listener

@Component
public class PackApplicationListener implements ApplicationListener<PackApplicationEvent> {
    @Override
    public void onApplicationEvent(PackApplicationEvent event) {
        System.out.printf("Received event message: %s, data: %s%n",
                event.getMessage(), event.getSource().toString());
    }
}

The listener simply prints the received information.

1.3 Publishing the Event

@Component
public class EventProcessor {
    public EventProcessor(ApplicationEventPublisher eventPublisher) {
        Thread t = new Thread(() -> {
            eventPublisher.publishEvent(
                new PackApplicationEvent("Custom Event", EventProcessor.this));
        });
        t.start();
        try {
            System.out.println("Thread started, waiting for completion...");
            t.join();
        } catch (InterruptedException e) {
            System.err.printf("Thread interrupted: %s, error: %s%n",
                    Thread.currentThread().getName(), e.getMessage());
        }
    }
}

This bean creates a new thread in its constructor, publishes the event, and waits for the thread to finish.

Running the program prints the listener output, but the application then hangs. A jstack dump shows that the main thread holds the DefaultSingletonBeanRegistry.singletonObjects lock while the new thread tries to acquire the same lock to create the listener bean, resulting in a deadlock.

2. Solutions

2.1 Use SmartInitializingSingleton

Do not publish events in a constructor. Implement SmartInitializingSingleton so that event publishing occurs after all singleton beans are created.

@Component
public class EventProcessor implements SmartInitializingSingleton {
    private final ApplicationEventPublisher eventPublisher;
    public EventProcessor(ApplicationEventPublisher eventPublisher) {
        this.eventPublisher = eventPublisher;
    }
    @Override
    public void afterSingletonsInstantiated() {
        Thread t = new Thread(() -> {
            eventPublisher.publishEvent(
                new PackApplicationEvent("Custom Event", this));
        });
        t.start();
        try {
            t.join();
        } catch (InterruptedException e) {
            System.err.printf("Thread interrupted: %s, error: %s%n",
                    Thread.currentThread().getName(), e.getMessage());
        }
    }
}

With this change the container starts normally and the event is published and listened to without deadlock.

The afterSingletonsInstantiated method is invoked by DefaultListableBeanFactory.preInstantiateSingletons() after all singleton beans have been created.

2.2 Upgrade Spring Version

Another option is to use Spring 6.2 (snapshot) which initializes beans with a multithreaded approach, avoiding the lock contention that caused the deadlock.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Javadeadlockspring-bootcustom-eventsmart-initializing-singleton
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.