AbstractBeanFactory#markBeanAsCreated is that

    protected void markBeanAsCreated(String beanName) {
        if (!this.alreadyCreated.contains(beanName)) {
            synchronized (this.mergedBeanDefinitions) {
                if (!this.alreadyCreated.contains(beanName)) {
                    // Let the bean definition get re-merged now that we're actually creating
                    // the bean... just in case some of its metadata changed in the meantime.
                    clearMergedBeanDefinition(beanName);
                    this.alreadyCreated.add(beanName);
                }
            }
        }
    }

I think MergedBeanDefinitions have no relationship to alreadyCreated,so I think I should change the code to look like this:

protected void markBeanAsCreated(String beanName) {
        synchronized (this.alreadyCreated) {
                if (!this.alreadyCreated.contains(beanName)) {
                    // Let the bean definition get re-merged now that we're actually creating
                    // the bean... just in case some of its metadata changed in the meantime.
                    clearMergedBeanDefinition(beanName);
                    this.alreadyCreated.add(beanName);
                }
            }

    }

If I understand correctly, I would like to submit a PR,Thanks.

Comment From: jhoeller

This is intentional, just not very obvious I guess: The main purpose of this code is controlled clearing of the merged bean definition cache, so we're intentionally reusing the same lock as in getMergedBeanDefinition there.

alreadyCreated itself is a ConcurrentHashMap underneath its Set decorator, so contains can be called outside of a lock (which reduces thread contention on access). Jus for structural modifications to alreadyCreated, we're reusing the mergedBeanDefinitions lock in order to align its state with the mergedBeanDefinitions structure itself.