diff --git a/common/src/main/java/org/sonarlint/intellij/common/util/SonarLintUtils.java b/common/src/main/java/org/sonarlint/intellij/common/util/SonarLintUtils.java
index 60b30df874..861d7effee 100644
--- a/common/src/main/java/org/sonarlint/intellij/common/util/SonarLintUtils.java
+++ b/common/src/main/java/org/sonarlint/intellij/common/util/SonarLintUtils.java
@@ -119,6 +119,7 @@ public static VirtualFile getSelectedFile(Project project) {
     if (project.isDisposed()) {
       return null;
     }
+    ApplicationManager.getApplication().assertIsDispatchThread();
     var editorManager = FileEditorManager.getInstance(project);
 
     var editor = editorManager.getSelectedTextEditor();
diff --git a/src/main/java/org/sonarlint/intellij/SonarLintIntelliJClient.kt b/src/main/java/org/sonarlint/intellij/SonarLintIntelliJClient.kt
index 1f37d6743c..0dcc705243 100644
--- a/src/main/java/org/sonarlint/intellij/SonarLintIntelliJClient.kt
+++ b/src/main/java/org/sonarlint/intellij/SonarLintIntelliJClient.kt
@@ -594,19 +594,12 @@ object SonarLintIntelliJClient : SonarLintRpcClientDelegate {
                 if (project == null || project.isDisposed) return@forEach
                 getService(project, AnalysisReadinessCache::class.java).isReady = areReadyForAnalysis
                 if (areReadyForAnalysis) {
-                    val findingToShow = getService(project, OpenInIdeFindingCache::class.java).finding
-                    if (findingToShow != null && !getService(project, OpenInIdeFindingCache::class.java).analysisQueued) {
-                        getService(project, AnalysisSubmitter::class.java).analyzeFileAndTrySelectFinding(findingToShow)
-                    }
-
-                    if (ApplicationManager.getApplication().isUnitTestMode) {
-                        runOnPooledThread(project) {
-                            getService(project, AnalysisSubmitter::class.java).autoAnalyzeOpenFilesForModule(
-                                TriggerType.BINDING_UPDATE,
-                                module
-                            )
+                    runOnPooledThread(project) {
+                        val findingToShow = getService(project, OpenInIdeFindingCache::class.java).finding
+                        if (findingToShow != null && !getService(project, OpenInIdeFindingCache::class.java).analysisQueued) {
+                            getService(project, AnalysisSubmitter::class.java).analyzeFileAndTrySelectFinding(findingToShow)
                         }
-                    } else {
+
                         getService(project, AnalysisSubmitter::class.java).autoAnalyzeOpenFilesForModule(
                             TriggerType.BINDING_UPDATE,
                             module
diff --git a/src/main/java/org/sonarlint/intellij/StartServicesOnProjectOpened.java b/src/main/java/org/sonarlint/intellij/StartServicesOnProjectOpened.java
index 21897e6895..1b774577c9 100644
--- a/src/main/java/org/sonarlint/intellij/StartServicesOnProjectOpened.java
+++ b/src/main/java/org/sonarlint/intellij/StartServicesOnProjectOpened.java
@@ -30,6 +30,7 @@
 import org.sonarlint.intellij.trigger.EditorChangeTrigger;
 
 import static org.sonarlint.intellij.common.util.SonarLintUtils.getService;
+import static org.sonarlint.intellij.util.ThreadUtilsKt.runOnPooledThread;
 
 public class StartServicesOnProjectOpened implements StartupActivity {
 
@@ -38,10 +39,12 @@ public void runActivity(@NotNull Project project) {
     if (ApplicationManager.getApplication().isUnitTestMode()) {
       return;
     }
-    getService(EditorFileChangeListener.class).startListening();
-    getService(project, EditorChangeTrigger.class).onProjectOpened();
-    getService(BackendService.class).projectOpened(project);
-    getService(project, SecurityHotspotsRefreshTrigger.class).subscribeToTriggeringEvents();
-    getService(project, PromotionProvider.class).subscribeToTriggeringEvents();
+    runOnPooledThread(project, () -> {
+      getService(EditorFileChangeListener.class).startListening();
+      getService(project, EditorChangeTrigger.class).onProjectOpened();
+      getService(BackendService.class).projectOpened(project);
+      getService(project, SecurityHotspotsRefreshTrigger.class).subscribeToTriggeringEvents();
+      getService(project, PromotionProvider.class).subscribeToTriggeringEvents();
+    });
   }
 }
diff --git a/src/main/java/org/sonarlint/intellij/actions/ClearCurrentFileIssuesAction.java b/src/main/java/org/sonarlint/intellij/actions/ClearCurrentFileIssuesAction.java
index 9199f1be18..39ae1c177c 100644
--- a/src/main/java/org/sonarlint/intellij/actions/ClearCurrentFileIssuesAction.java
+++ b/src/main/java/org/sonarlint/intellij/actions/ClearCurrentFileIssuesAction.java
@@ -64,6 +64,7 @@ public void actionPerformed(AnActionEvent e) {
   }
 
   public Collection<PsiFile> findFiles(Project project, VirtualFile[] files) {
+    ApplicationManager.getApplication().assertReadAccessAllowed();
     var psiManager = PsiManager.getInstance(project);
     var psiFiles = new ArrayList<PsiFile>(files.length);
 
diff --git a/src/main/java/org/sonarlint/intellij/actions/DisableRuleAction.java b/src/main/java/org/sonarlint/intellij/actions/DisableRuleAction.java
index 156a0533e5..1ef9b2a813 100644
--- a/src/main/java/org/sonarlint/intellij/actions/DisableRuleAction.java
+++ b/src/main/java/org/sonarlint/intellij/actions/DisableRuleAction.java
@@ -24,7 +24,6 @@
 import com.intellij.openapi.project.Project;
 import org.sonarlint.intellij.analysis.AnalysisStatus;
 import org.sonarlint.intellij.analysis.AnalysisSubmitter;
-import org.sonarlint.intellij.common.util.SonarLintUtils;
 import org.sonarlint.intellij.core.BackendService;
 import org.sonarlint.intellij.trigger.TriggerType;
 
@@ -32,6 +31,7 @@
 import static org.sonarlint.intellij.config.Settings.getGlobalSettings;
 import static org.sonarlint.intellij.config.Settings.getSettingsFor;
 import static org.sonarlint.intellij.util.DataKeys.ISSUE_DATA_KEY;
+import static org.sonarlint.intellij.util.ThreadUtilsKt.runOnPooledThread;
 
 public class DisableRuleAction extends AbstractSonarAction {
 
@@ -62,7 +62,7 @@ public void actionPerformed(AnActionEvent e) {
     var issue = e.getData(ISSUE_DATA_KEY);
     if (issue != null) {
       disableRule(issue.getRuleKey());
-      SonarLintUtils.getService(project, AnalysisSubmitter.class).autoAnalyzeOpenFiles(TriggerType.BINDING_UPDATE);
+      runOnPooledThread(project, () -> getService(project, AnalysisSubmitter.class).autoAnalyzeOpenFiles(TriggerType.BINDING_UPDATE));
     }
   }
 
diff --git a/src/main/java/org/sonarlint/intellij/actions/ExcludeFileAction.kt b/src/main/java/org/sonarlint/intellij/actions/ExcludeFileAction.kt
index e17546fd85..efb7f788b1 100644
--- a/src/main/java/org/sonarlint/intellij/actions/ExcludeFileAction.kt
+++ b/src/main/java/org/sonarlint/intellij/actions/ExcludeFileAction.kt
@@ -27,12 +27,11 @@ import com.intellij.openapi.project.Project
 import com.intellij.openapi.vfs.VirtualFile
 import org.sonarlint.intellij.analysis.AnalysisStatus
 import org.sonarlint.intellij.analysis.AnalysisSubmitter
-import org.sonarlint.intellij.common.util.SonarLintUtils
+import org.sonarlint.intellij.common.util.SonarLintUtils.getService
 import org.sonarlint.intellij.config.Settings
 import org.sonarlint.intellij.config.project.ExclusionItem
 import org.sonarlint.intellij.messages.ProjectConfigurationListener
 import org.sonarlint.intellij.trigger.TriggerType
-import org.sonarlint.intellij.ui.UiUtils.Companion.runOnUiThread
 import org.sonarlint.intellij.util.SonarLintAppUtils
 import org.sonarlint.intellij.util.runOnPooledThread
 
@@ -64,11 +63,9 @@ class ExcludeFileAction : AbstractSonarAction {
             if (newExclusions.isNotEmpty()) {
                 exclusions.addAll(newExclusions)
                 settings.fileExclusions = exclusions
-                SonarLintUtils.getService(project, AnalysisSubmitter::class.java).autoAnalyzeOpenFiles(TriggerType.CONFIG_CHANGE)
-                runOnUiThread(project) {
-                    val projectListener = project.messageBus.syncPublisher(ProjectConfigurationListener.TOPIC)
-                    projectListener.changed(settings)
-                }
+                getService(project, AnalysisSubmitter::class.java).autoAnalyzeOpenFiles(TriggerType.CONFIG_CHANGE)
+                val projectListener = project.messageBus.syncPublisher(ProjectConfigurationListener.TOPIC)
+                projectListener.changed(settings)
             }
         }
     }
diff --git a/src/main/java/org/sonarlint/intellij/actions/MarkAsResolvedAction.kt b/src/main/java/org/sonarlint/intellij/actions/MarkAsResolvedAction.kt
index 102bb32feb..9be2fe978c 100644
--- a/src/main/java/org/sonarlint/intellij/actions/MarkAsResolvedAction.kt
+++ b/src/main/java/org/sonarlint/intellij/actions/MarkAsResolvedAction.kt
@@ -46,6 +46,7 @@ import org.sonarlint.intellij.ui.resolve.MarkAsResolvedDialog
 import org.sonarlint.intellij.util.DataKeys.Companion.ISSUE_DATA_KEY
 import org.sonarlint.intellij.util.DataKeys.Companion.TAINT_VULNERABILITY_DATA_KEY
 import org.sonarlint.intellij.util.SonarLintAppUtils.findModuleForFile
+import org.sonarlint.intellij.util.computeOnPooledThread
 import org.sonarlint.intellij.util.runOnPooledThread
 import org.sonarsource.sonarlint.core.client.utils.IssueResolutionStatus
 import org.sonarsource.sonarlint.core.rpc.protocol.backend.issue.CheckStatusChangePermittedResponse
@@ -86,7 +87,9 @@ class MarkAsResolvedAction(
                 project, "No module could be found for this file"
             )
             val serverKey = issue.getServerKey() ?: issue.getId().toString()
-            val response = checkPermission(project, connection, serverKey) ?: return
+            val response = computeOnPooledThread(project, "Checking permission to mark issue as resolved") {
+                checkPermission(project, connection, serverKey)
+            } ?: return
 
             if (response.isPermitted) {
                 runOnUiThread(project) {
@@ -96,7 +99,9 @@ class MarkAsResolvedAction(
                         response,
                     ).chooseResolution() ?: return@runOnUiThread
                     if (confirm(project, connection.productName, resolution.newStatus)) {
-                        markAsResolved(project, module, issue, resolution, serverKey)
+                        runOnPooledThread(project) {
+                            markAsResolved(project, module, issue, resolution, serverKey)
+                        }
                     }
                 }
             } else {
diff --git a/src/main/java/org/sonarlint/intellij/actions/OpenSecurityHotspotInBrowserAction.kt b/src/main/java/org/sonarlint/intellij/actions/OpenSecurityHotspotInBrowserAction.kt
index 97548a783c..1d9edfbb00 100644
--- a/src/main/java/org/sonarlint/intellij/actions/OpenSecurityHotspotInBrowserAction.kt
+++ b/src/main/java/org/sonarlint/intellij/actions/OpenSecurityHotspotInBrowserAction.kt
@@ -29,6 +29,7 @@ import org.sonarlint.intellij.core.BackendService
 import org.sonarlint.intellij.core.ProjectBindingManager
 import org.sonarlint.intellij.finding.hotspot.LiveSecurityHotspot
 import org.sonarlint.intellij.util.SonarLintAppUtils.findModuleForFile
+import org.sonarlint.intellij.util.runOnPooledThread
 
 class OpenSecurityHotspotInBrowserAction : AbstractSonarAction(
   "Open In Browser",
@@ -57,7 +58,9 @@ class OpenSecurityHotspotInBrowserAction : AbstractSonarAction(
     val key = securityHotspot?.getServerKey() ?: return
     val localFile = securityHotspot.file()
     val localFileModule = findModuleForFile(localFile, project) ?: return
-    getService(BackendService::class.java).openHotspotInBrowser(localFileModule, key)
+    runOnPooledThread(project) {
+      getService(BackendService::class.java).openHotspotInBrowser(localFileModule, key)
+    }
   }
 
   private fun serverConnection(project: Project): ServerConnection? = getService(project, ProjectBindingManager::class.java).tryGetServerConnection().orElse(null)
diff --git a/src/main/java/org/sonarlint/intellij/actions/RefreshTaintVulnerabilitiesAction.kt b/src/main/java/org/sonarlint/intellij/actions/RefreshTaintVulnerabilitiesAction.kt
index e9d9a8881f..e442dced12 100644
--- a/src/main/java/org/sonarlint/intellij/actions/RefreshTaintVulnerabilitiesAction.kt
+++ b/src/main/java/org/sonarlint/intellij/actions/RefreshTaintVulnerabilitiesAction.kt
@@ -26,12 +26,16 @@ import org.sonarlint.intellij.analysis.AnalysisStatus
 import org.sonarlint.intellij.common.util.SonarLintUtils.getService
 import org.sonarlint.intellij.core.BackendService
 import org.sonarlint.intellij.core.ProjectBindingManager
+import org.sonarlint.intellij.util.runOnPooledThread
 
 class RefreshTaintVulnerabilitiesAction(text: String = "Refresh") : AbstractSonarAction(text, "Refresh taint vulnerabilities for open files", AllIcons.Actions.Refresh) {
   override fun isEnabled(e: AnActionEvent, project: Project, status: AnalysisStatus) = getService(project, ProjectBindingManager::class.java).isBindingValid
 
   override fun actionPerformed(e: AnActionEvent) {
     val project = e.project ?: return
-    getService(BackendService::class.java).refreshTaintVulnerabilities(project)
+
+    runOnPooledThread {
+      getService(BackendService::class.java).refreshTaintVulnerabilities(project)
+    }
   }
 }
diff --git a/src/main/java/org/sonarlint/intellij/actions/ReopenIssueAction.kt b/src/main/java/org/sonarlint/intellij/actions/ReopenIssueAction.kt
index a3ff3db42e..215d611abf 100644
--- a/src/main/java/org/sonarlint/intellij/actions/ReopenIssueAction.kt
+++ b/src/main/java/org/sonarlint/intellij/actions/ReopenIssueAction.kt
@@ -43,6 +43,7 @@ import org.sonarlint.intellij.finding.issue.vulnerabilities.LocalTaintVulnerabil
 import org.sonarlint.intellij.notifications.SonarLintProjectNotifications
 import org.sonarlint.intellij.util.DataKeys
 import org.sonarlint.intellij.util.SonarLintAppUtils.findModuleForFile
+import org.sonarlint.intellij.util.runOnPooledThread
 
 private const val SKIP_CONFIRM_REOPEN_DIALOG_PROPERTY = "SonarLint.reopenIssue.hideConfirmation"
 
@@ -73,7 +74,7 @@ class ReopenIssueAction(private var issue: LiveIssue? = null) : AbstractSonarAct
             serverKey ?: return displayNotificationError(project, "The issue key could not be found")
 
             if (confirm(project, connection.productName)) {
-                reopenFinding(project, module, issue, serverKey)
+                runOnPooledThread(project) { reopenFinding(project, module, issue, serverKey) }
             }
         }
 
diff --git a/src/main/java/org/sonarlint/intellij/actions/ReviewSecurityHotspotAction.kt b/src/main/java/org/sonarlint/intellij/actions/ReviewSecurityHotspotAction.kt
index dcfc93fead..a0bb616549 100644
--- a/src/main/java/org/sonarlint/intellij/actions/ReviewSecurityHotspotAction.kt
+++ b/src/main/java/org/sonarlint/intellij/actions/ReviewSecurityHotspotAction.kt
@@ -42,6 +42,7 @@ import org.sonarlint.intellij.tasks.FutureAwaitingTask
 import org.sonarlint.intellij.ui.UiUtils.Companion.runOnUiThread
 import org.sonarlint.intellij.ui.review.ReviewSecurityHotspotDialog
 import org.sonarlint.intellij.util.SonarLintAppUtils.findModuleForFile
+import org.sonarlint.intellij.util.computeOnPooledThread
 import org.sonarlint.intellij.util.runOnPooledThread
 import org.sonarsource.sonarlint.core.commons.HotspotReviewStatus
 import org.sonarsource.sonarlint.core.rpc.protocol.backend.hotspot.CheckStatusChangePermittedResponse
@@ -103,7 +104,10 @@ class ReviewSecurityHotspotAction(private var serverFindingKey: String? = null,
             "No module could be found for this file"
         )
 
-        val response = checkPermission(project, connection, hotspotKey) ?: return
+        val response = computeOnPooledThread(project, "Checking permission to mark issue as resolved") {
+            checkPermission(project, connection, hotspotKey)
+        } ?: return
+
         val newStatus = HotspotStatus.valueOf(currentStatus.name)
         runOnUiThread(project) {
             if (ReviewSecurityHotspotDialog(project, connection.productName, module, hotspotKey, response, newStatus).showAndGet()) {
diff --git a/src/main/java/org/sonarlint/intellij/actions/SonarAnalyzeAllFilesAction.java b/src/main/java/org/sonarlint/intellij/actions/SonarAnalyzeAllFilesAction.java
index a6b39fc63f..c507fff8ce 100644
--- a/src/main/java/org/sonarlint/intellij/actions/SonarAnalyzeAllFilesAction.java
+++ b/src/main/java/org/sonarlint/intellij/actions/SonarAnalyzeAllFilesAction.java
@@ -37,6 +37,7 @@
 
 import static org.sonarlint.intellij.common.util.SonarLintUtils.getService;
 import static org.sonarlint.intellij.util.ProjectUtils.hasFiles;
+import static org.sonarlint.intellij.util.ThreadUtilsKt.runOnPooledThread;
 
 public class SonarAnalyzeAllFilesAction extends AbstractSonarAction {
   private static final String HIDE_WARNING_PROPERTY = "SonarLint.analyzeAllFiles.hideWarning";
@@ -70,7 +71,7 @@ public void actionPerformed(AnActionEvent e) {
       return;
     }
 
-    SonarLintUtils.getService(project, AnalysisSubmitter.class).analyzeAllFiles();
+    runOnPooledThread(project, () -> SonarLintUtils.getService(project, AnalysisSubmitter.class).analyzeAllFiles());
   }
 
   static boolean userConfirmed(Project project) {
diff --git a/src/main/java/org/sonarlint/intellij/actions/SonarAnalyzeChangedFilesAction.java b/src/main/java/org/sonarlint/intellij/actions/SonarAnalyzeChangedFilesAction.java
index 78b15b1813..b541d136f2 100644
--- a/src/main/java/org/sonarlint/intellij/actions/SonarAnalyzeChangedFilesAction.java
+++ b/src/main/java/org/sonarlint/intellij/actions/SonarAnalyzeChangedFilesAction.java
@@ -25,12 +25,13 @@
 import com.intellij.openapi.vcs.changes.ChangeListManager;
 import javax.swing.Icon;
 import org.jetbrains.annotations.Nullable;
-import org.sonarlint.intellij.analysis.AnalysisSubmitter;
 import org.sonarlint.intellij.analysis.AnalysisStatus;
+import org.sonarlint.intellij.analysis.AnalysisSubmitter;
 import org.sonarlint.intellij.common.util.SonarLintUtils;
 import org.sonarlint.intellij.core.BackendService;
 
 import static org.sonarlint.intellij.common.util.SonarLintUtils.getService;
+import static org.sonarlint.intellij.util.ThreadUtilsKt.runOnPooledThread;
 
 public class SonarAnalyzeChangedFilesAction extends AbstractSonarAction {
   public SonarAnalyzeChangedFilesAction() {
@@ -62,6 +63,6 @@ protected boolean isVisible(AnActionEvent e) {
       return;
     }
 
-    SonarLintUtils.getService(project, AnalysisSubmitter.class).analyzeVcsChangedFiles();
+    runOnPooledThread(project, () -> SonarLintUtils.getService(project, AnalysisSubmitter.class).analyzeVcsChangedFiles());
   }
 }
diff --git a/src/main/java/org/sonarlint/intellij/actions/SonarAnalyzeFilesAction.java b/src/main/java/org/sonarlint/intellij/actions/SonarAnalyzeFilesAction.java
index bfba9b119e..707b46a796 100644
--- a/src/main/java/org/sonarlint/intellij/actions/SonarAnalyzeFilesAction.java
+++ b/src/main/java/org/sonarlint/intellij/actions/SonarAnalyzeFilesAction.java
@@ -37,6 +37,7 @@
 import org.sonarlint.intellij.core.BackendService;
 
 import static org.sonarlint.intellij.common.util.SonarLintUtils.getService;
+import static org.sonarlint.intellij.util.ThreadUtilsKt.runOnPooledThread;
 
 public class SonarAnalyzeFilesAction extends AbstractSonarAction {
 
@@ -88,7 +89,7 @@ public void actionPerformed(AnActionEvent e) {
       })
       .collect(Collectors.toSet());
 
-    getService(project, AnalysisSubmitter.class).analyzeFilesOnUserAction(fileSet, e);
+    runOnPooledThread(project, () -> getService(project, AnalysisSubmitter.class).analyzeFilesOnUserAction(fileSet, e));
   }
 
   private static class CollectFilesVisitor extends VirtualFileVisitor {
diff --git a/src/main/java/org/sonarlint/intellij/actions/SonarFocusOnNewCode.kt b/src/main/java/org/sonarlint/intellij/actions/SonarFocusOnNewCode.kt
index 7b4f83a234..163180715d 100644
--- a/src/main/java/org/sonarlint/intellij/actions/SonarFocusOnNewCode.kt
+++ b/src/main/java/org/sonarlint/intellij/actions/SonarFocusOnNewCode.kt
@@ -24,7 +24,7 @@ import com.intellij.openapi.actionSystem.Presentation
 import com.intellij.openapi.project.Project
 import org.sonarlint.intellij.cayc.CleanAsYouCodeService
 import org.sonarlint.intellij.common.util.SonarLintUtils.getService
-import org.sonarlint.intellij.config.Settings
+import org.sonarlint.intellij.util.runOnPooledThread
 
 class SonarFocusOnNewCode : AbstractSonarToggleAction() {
 
@@ -32,7 +32,7 @@ class SonarFocusOnNewCode : AbstractSonarToggleAction() {
             ?: false
 
     override fun setSelected(e: AnActionEvent, isSelected: Boolean) {
-        getService(CleanAsYouCodeService::class.java).setFocusOnNewCode(isSelected)
+        e.project?.let { runOnPooledThread(it) { getService(CleanAsYouCodeService::class.java).setFocusOnNewCode(isSelected) } }
     }
 
     override fun updatePresentation(project: Project, presentation: Presentation) {
diff --git a/src/main/java/org/sonarlint/intellij/actions/SonarLintToolWindow.java b/src/main/java/org/sonarlint/intellij/actions/SonarLintToolWindow.java
index acb3572131..72ca79bfe9 100644
--- a/src/main/java/org/sonarlint/intellij/actions/SonarLintToolWindow.java
+++ b/src/main/java/org/sonarlint/intellij/actions/SonarLintToolWindow.java
@@ -64,6 +64,7 @@
 
 import static org.sonarlint.intellij.common.util.SonarLintUtils.getService;
 import static org.sonarlint.intellij.ui.UiUtils.runOnUiThread;
+import static org.sonarlint.intellij.util.ThreadUtilsKt.runOnPooledThread;
 
 @Service(Service.Level.PROJECT)
 public final class SonarLintToolWindow implements ContentManagerListener, ProjectBindingListener {
@@ -81,6 +82,7 @@ public SonarLintToolWindow(Project project) {
    * Must run in EDT
    */
   public void openReportTab(AnalysisResult analysisResult) {
+    ApplicationManager.getApplication().assertIsDispatchThread();
     this.<ReportPanel>openTab(SonarLintToolWindowFactory.REPORT_TAB_TITLE, panel -> panel.updateFindings(analysisResult));
   }
 
@@ -123,13 +125,13 @@ public void filterSecurityHotspotTab(boolean isResolved) {
   }
 
   private <T> void openTab(String displayName, Consumer<T> tabPanelConsumer) {
-    var toolWindow = updateTab(displayName, tabPanelConsumer);
+    var toolWindow = updateTabAndGet(displayName, tabPanelConsumer);
     if (toolWindow != null) {
       toolWindow.show(() -> selectTab(toolWindow, displayName));
     }
   }
 
-  private <T> ToolWindow updateTab(String displayName, Consumer<T> tabPanelConsumer) {
+  private <T> ToolWindow updateTabAndGet(String displayName, Consumer<T> tabPanelConsumer) {
     ApplicationManager.getApplication().assertIsDispatchThread();
     var toolWindow = getToolWindow();
     if (toolWindow != null) {
@@ -137,12 +139,26 @@ private <T> ToolWindow updateTab(String displayName, Consumer<T> tabPanelConsume
       var content = contentManager.findContent(displayName);
       if (content != null) {
         var panel = (T) content.getComponent();
-        tabPanelConsumer.accept(panel);
+        runOnPooledThread(project, () -> tabPanelConsumer.accept(panel));
       }
     }
     return toolWindow;
   }
 
+  private <T> void updateTab(String displayName, Consumer<T> tabPanelConsumer) {
+    var toolWindow = getToolWindow();
+    if (toolWindow != null) {
+      runOnUiThread(project, () -> {
+        var contentManager = toolWindow.getContentManager();
+        var content = contentManager.findContent(displayName);
+        if (content != null) {
+          var panel = (T) content.getComponent();
+          runOnPooledThread(project, () -> tabPanelConsumer.accept(panel));
+        }
+      });
+    }
+  }
+
   public void filterCurrentFileTab(boolean isResolved) {
     this.<CurrentFilePanel>updateTab(SonarLintToolWindowFactory.CURRENT_FILE_TAB_TITLE, panel -> panel.allowResolvedIssues(isResolved));
   }
@@ -159,6 +175,7 @@ public void filterTaintVulnerabilityTab(boolean isResolved) {
    * Must run in EDT
    */
   public void openCurrentFileTab() {
+    ApplicationManager.getApplication().assertIsDispatchThread();
     openTab(SonarLintToolWindowFactory.CURRENT_FILE_TAB_TITLE);
   }
 
@@ -207,7 +224,7 @@ public void refreshViews() {
     var hotspotContent = getSecurityHotspotContent();
     if (hotspotContent != null) {
       var hotspotsPanel = (SecurityHotspotsPanel) hotspotContent.getComponent();
-      hotspotContent.setDisplayName(buildTabName(hotspotsPanel.refreshView(), SonarLintToolWindowFactory.SECURITY_HOTSPOTS_TAB_TITLE));
+      runOnUiThread(project, () -> hotspotContent.setDisplayName(buildTabName(hotspotsPanel.refreshView(), SonarLintToolWindowFactory.SECURITY_HOTSPOTS_TAB_TITLE)));
     }
 
     var taintContent = getTaintVulnerabilitiesContent();
@@ -289,7 +306,8 @@ private static void selectTab(ToolWindow toolWindow, String tabId) {
   }
 
   public void updateCurrentFileTab(@Nullable VirtualFile selectedFile, @Nullable Collection<LiveIssue> issues) {
-    this.<CurrentFilePanel>updateTab(SonarLintToolWindowFactory.CURRENT_FILE_TAB_TITLE, panel -> panel.update(selectedFile, issues));
+    this.<CurrentFilePanel>updateTab(SonarLintToolWindowFactory.CURRENT_FILE_TAB_TITLE,
+      panel -> runOnUiThread(project, () -> panel.update(selectedFile, issues)));
   }
 
   private void showIssue(LiveIssue liveIssue, Consumer<CurrentFilePanel> selectTab) {
@@ -401,8 +419,10 @@ public void updateOnTheFlySecurityHotspots(@NotNull Map<VirtualFile, Collection<
     var content = getSecurityHotspotContent();
     if (content != null) {
       var hotspotsPanel = (SecurityHotspotsPanel) content.getComponent();
-      var count = hotspotsPanel.updateHotspots(currentSecurityHotspotsPerOpenFile);
-      content.setDisplayName(buildTabName(count, SonarLintToolWindowFactory.SECURITY_HOTSPOTS_TAB_TITLE));
+      runOnUiThread(project, () -> {
+        var count = hotspotsPanel.updateHotspots(currentSecurityHotspotsPerOpenFile);
+        content.setDisplayName(buildTabName(count, SonarLintToolWindowFactory.SECURITY_HOTSPOTS_TAB_TITLE));
+      });
     }
   }
 
@@ -441,6 +461,6 @@ private Content getTaintVulnerabilitiesContent() {
 
   @Override
   public void bindingChanged() {
-    runOnUiThread(project, this::refreshViews);
+    runOnPooledThread(project, this::refreshViews);
   }
 }
diff --git a/src/main/java/org/sonarlint/intellij/analysis/OnTheFlyFindingsHolder.kt b/src/main/java/org/sonarlint/intellij/analysis/OnTheFlyFindingsHolder.kt
index 945d2527d4..24c9d85e67 100644
--- a/src/main/java/org/sonarlint/intellij/analysis/OnTheFlyFindingsHolder.kt
+++ b/src/main/java/org/sonarlint/intellij/analysis/OnTheFlyFindingsHolder.kt
@@ -58,8 +58,8 @@ class OnTheFlyFindingsHolder(private val project: Project) : FileEditorManagerLi
         updateViewsWithNewFindings(intermediateResult.findings)
 
     private fun updateViewsWithNewFindings(findings: LiveFindings) {
-        runOnUiThread(project) {
-            if (selectedFile == null) {
+        if (selectedFile == null) {
+            runOnUiThread(project) {
                 selectedFile = SonarLintUtils.getSelectedFile(project)
             }
         }
@@ -69,11 +69,9 @@ class OnTheFlyFindingsHolder(private val project: Project) : FileEditorManagerLi
             currentIssuesPerOpenFile.putAll(issuesPerFile)
             currentSecurityHotspotsPerOpenFile.putAll(securityHotspotsPerFile)
         }
-        runOnUiThread(project) {
-            updateCurrentFileTab()
-            updateSecurityHotspots()
-            getService(project, CodeAnalyzerRestarter::class.java).refreshFiles(findings.onlyFor(openedFiles).filesInvolved)
-        }
+        updateCurrentFileTab()
+        updateSecurityHotspots()
+        getService(project, CodeAnalyzerRestarter::class.java).refreshFiles(findings.onlyFor(openedFiles).filesInvolved)
     }
 
     fun updateViewsWithNewIssues(module: Module, raisedIssues: Map<URI, List<RaisedIssueDto>>) {
@@ -85,13 +83,13 @@ class OnTheFlyFindingsHolder(private val project: Project) : FileEditorManagerLi
             virtualFile to liveIssues
         }.toMap()
         currentIssuesPerOpenFile.putAll(issues)
-        runOnUiThread(project) {
-            if (selectedFile == null) {
+        if (selectedFile == null) {
+            runOnUiThread(project) {
                 selectedFile = SonarLintUtils.getSelectedFile(project)
             }
-            updateCurrentFileTab()
-            getService(project, CodeAnalyzerRestarter::class.java).refreshFiles(issues.keys)
         }
+        updateCurrentFileTab()
+        getService(project, CodeAnalyzerRestarter::class.java).refreshFiles(issues.keys)
     }
 
     fun updateViewsWithNewSecurityHotspots(module: Module, raisedSecurityHotspots: Map<URI, List<RaisedHotspotDto>>) {
@@ -103,13 +101,13 @@ class OnTheFlyFindingsHolder(private val project: Project) : FileEditorManagerLi
             virtualFile to liveIssues
         }.toMap().filterKeys { openFiles.contains(it) }
         currentSecurityHotspotsPerOpenFile.putAll(securityHotspots)
-        runOnUiThread(project) {
-            if (selectedFile == null) {
+        if (selectedFile == null) {
+            runOnUiThread(project) {
                 selectedFile = SonarLintUtils.getSelectedFile(project)
             }
-            updateSecurityHotspots()
-            getService(project, CodeAnalyzerRestarter::class.java).refreshFiles(securityHotspots.keys)
         }
+        updateSecurityHotspots()
+        getService(project, CodeAnalyzerRestarter::class.java).refreshFiles(securityHotspots.keys)
     }
 
     override fun selectionChanged(event: FileEditorManagerEvent) {
diff --git a/src/main/java/org/sonarlint/intellij/cayc/CleanAsYouCodeService.kt b/src/main/java/org/sonarlint/intellij/cayc/CleanAsYouCodeService.kt
index d86f3cd698..c3fb94ac01 100644
--- a/src/main/java/org/sonarlint/intellij/cayc/CleanAsYouCodeService.kt
+++ b/src/main/java/org/sonarlint/intellij/cayc/CleanAsYouCodeService.kt
@@ -28,6 +28,7 @@ import org.sonarlint.intellij.common.util.SonarLintUtils.getService
 import org.sonarlint.intellij.config.Settings.getGlobalSettings
 import org.sonarlint.intellij.config.global.SonarLintGlobalSettings
 import org.sonarlint.intellij.core.BackendService
+import org.sonarlint.intellij.util.runOnPooledThread
 
 @Service(Service.Level.APP)
 class CleanAsYouCodeService {
@@ -46,11 +47,13 @@ class CleanAsYouCodeService {
     private fun refresh(settings: SonarLintGlobalSettings, isFocusOnNewCode: Boolean) {
         if (settings.isFocusOnNewCode != isFocusOnNewCode) {
             settings.isFocusOnNewCode = isFocusOnNewCode
-            getService(BackendService::class.java).triggerTelemetryForFocusOnNewCode()
-            ProjectManager.getInstance().openProjects.forEach { project ->
-                if (!project.isDisposed) {
-                    getService(project, SonarLintToolWindow::class.java).refreshViews()
-                    DaemonCodeAnalyzer.getInstance(project).restart()
+            runOnPooledThread {
+                getService(BackendService::class.java).triggerTelemetryForFocusOnNewCode()
+                ProjectManager.getInstance().openProjects.forEach { project ->
+                    if (!project.isDisposed) {
+                        getService(project, SonarLintToolWindow::class.java).refreshViews()
+                        DaemonCodeAnalyzer.getInstance(project).restart()
+                    }
                 }
             }
         }
diff --git a/src/main/java/org/sonarlint/intellij/cayc/NewCodePeriodCache.kt b/src/main/java/org/sonarlint/intellij/cayc/NewCodePeriodCache.kt
index 1936276aaf..546b46ce67 100644
--- a/src/main/java/org/sonarlint/intellij/cayc/NewCodePeriodCache.kt
+++ b/src/main/java/org/sonarlint/intellij/cayc/NewCodePeriodCache.kt
@@ -24,19 +24,22 @@ import com.intellij.openapi.project.Project
 import java.util.Locale
 import org.sonarlint.intellij.common.util.SonarLintUtils.getService
 import org.sonarlint.intellij.core.BackendService
+import org.sonarlint.intellij.util.runOnPooledThread
 
 @Service(Service.Level.PROJECT)
 class NewCodePeriodCache(private val project: Project) {
     var periodAsString: String = "(unknown code period)"
 
     fun refreshAsync() {
-        getService(BackendService::class.java).getNewCodePeriodText(project)
-            .thenAccept { period ->
-                periodAsString = period.replaceFirstChar { char ->
-                    char.lowercase(
-                        Locale.getDefault()
-                    )
+        runOnPooledThread(project) {
+            getService(BackendService::class.java).getNewCodePeriodText(project)
+                .thenAccept { period ->
+                    periodAsString = period.replaceFirstChar { char ->
+                        char.lowercase(
+                            Locale.getDefault()
+                        )
+                    }
                 }
-            }
+        }
     }
 }
diff --git a/src/main/java/org/sonarlint/intellij/config/global/SonarLintAboutPanel.java b/src/main/java/org/sonarlint/intellij/config/global/SonarLintAboutPanel.java
index c4c1bd46d3..a6d5e0127a 100644
--- a/src/main/java/org/sonarlint/intellij/config/global/SonarLintAboutPanel.java
+++ b/src/main/java/org/sonarlint/intellij/config/global/SonarLintAboutPanel.java
@@ -49,6 +49,7 @@
 
 import static org.sonarlint.intellij.documentation.SonarLintDocumentation.Intellij.BASE_DOCS_URL;
 import static org.sonarlint.intellij.ui.UiUtils.runOnUiThread;
+import static org.sonarlint.intellij.util.ThreadUtilsKt.runOnPooledThread;
 
 public class SonarLintAboutPanel implements ConfigurationPanel<SonarLintTelemetry> {
   private final JPanel panel;
@@ -234,10 +235,10 @@ public JComponent getComponent() {
   @Override
   public void load(SonarLintTelemetry telemetry) {
     // we could show a loader while getting the value
-    telemetry.enabled().thenAccept(enabled -> {
+    runOnPooledThread(() -> telemetry.enabled().thenAccept(enabled -> {
       telemetryInitiallyEnabled = enabled;
-      runOnUiThread(() -> enableTelemetryCheckBox.setSelected(enabled), ModalityState.stateForComponent(getComponent()));
-    });
+      runOnUiThread(ModalityState.defaultModalityState(), () -> enableTelemetryCheckBox.setSelected(enabled));
+    }));
   }
 
   @Override
diff --git a/src/main/java/org/sonarlint/intellij/config/global/SonarLintGlobalConfigurable.java b/src/main/java/org/sonarlint/intellij/config/global/SonarLintGlobalConfigurable.java
index d2e6bf5bb2..411533da5c 100644
--- a/src/main/java/org/sonarlint/intellij/config/global/SonarLintGlobalConfigurable.java
+++ b/src/main/java/org/sonarlint/intellij/config/global/SonarLintGlobalConfigurable.java
@@ -40,6 +40,7 @@
 import static org.sonarlint.intellij.common.util.SonarLintUtils.getService;
 import static org.sonarlint.intellij.config.Settings.getGlobalSettings;
 import static org.sonarlint.intellij.config.Settings.getSettingsFor;
+import static org.sonarlint.intellij.util.ThreadUtilsKt.runOnPooledThread;
 
 public class SonarLintGlobalConfigurable implements Configurable, Configurable.NoScroll {
   private static final int SETTINGS_TAB_INDEX = 0;
@@ -98,11 +99,13 @@ public void apply() {
     // Force reload of the node version and rules in case the nodejs path has been changed
     reset();
 
-    if (exclusionsModified || globalSettingsModified) {
-      analyzeOpenFiles(false);
-    } else if (rulesModified) {
-      analyzeOpenFiles(true);
-    }
+    runOnPooledThread(() -> {
+      if (exclusionsModified || globalSettingsModified) {
+        analyzeOpenFiles(false);
+      } else if (rulesModified) {
+        analyzeOpenFiles(true);
+      }
+    });
   }
 
   public static void analyzeOpenFiles(boolean unboundOnly) {
diff --git a/src/main/java/org/sonarlint/intellij/config/global/SonarLintGlobalOptionsPanel.java b/src/main/java/org/sonarlint/intellij/config/global/SonarLintGlobalOptionsPanel.java
index 4209e8480a..f9d2d43f93 100644
--- a/src/main/java/org/sonarlint/intellij/config/global/SonarLintGlobalOptionsPanel.java
+++ b/src/main/java/org/sonarlint/intellij/config/global/SonarLintGlobalOptionsPanel.java
@@ -140,15 +140,16 @@ public void load(SonarLintGlobalSettings model) {
   }
 
   private void loadNodeJsSettings() {
-    getService(BackendService.class).getAutoDetectedNodeJs().thenAccept(settings -> {
-      if (settings == null) {
-        this.nodeJsPath.getEmptyText().setText("Node.js not found");
-        this.nodeJsVersion.setText("N/A");
-      } else {
-        this.nodeJsPath.getEmptyText().setText(settings.getPath().toString());
-        this.nodeJsVersion.setText(settings.getVersion());
+      getService(BackendService.class).getAutoDetectedNodeJs().thenAccept(settings -> {
+        if (settings == null) {
+          this.nodeJsPath.getEmptyText().setText("Node.js not found");
+          this.nodeJsVersion.setText("N/A");
+        } else {
+          this.nodeJsPath.getEmptyText().setText(settings.getPath().toString());
+          this.nodeJsVersion.setText(settings.getVersion());
+        }
       }
-    });
+    );
   }
 
   @Override
diff --git a/src/main/java/org/sonarlint/intellij/config/global/rules/RuleConfigurationPanel.java b/src/main/java/org/sonarlint/intellij/config/global/rules/RuleConfigurationPanel.java
index 87ee7cae27..3f038b12a5 100644
--- a/src/main/java/org/sonarlint/intellij/config/global/rules/RuleConfigurationPanel.java
+++ b/src/main/java/org/sonarlint/intellij/config/global/rules/RuleConfigurationPanel.java
@@ -114,6 +114,7 @@
 import static org.sonarlint.intellij.config.Settings.getGlobalSettings;
 import static org.sonarlint.intellij.telemetry.LinkTelemetry.RULE_SELECTION_PAGE;
 import static org.sonarlint.intellij.ui.UiUtils.runOnUiThread;
+import static org.sonarlint.intellij.util.ThreadUtilsKt.runOnPooledThread;
 
 public class RuleConfigurationPanel implements Disposable, ConfigurationPanel<SonarLintGlobalSettings> {
   private static final String MAIN_SPLITTER_KEY = "sonarlint_rule_configuration_splitter";
@@ -236,7 +237,7 @@ public boolean isModified(SonarLintGlobalSettings settings) {
   }
 
   private void recomputeDirtyState() {
-    getService(BackendService.class).getListAllStandaloneRulesDefinitions()
+    runOnPooledThread(project, () -> getService(BackendService.class).getListAllStandaloneRulesDefinitions()
       .thenAcceptAsync(response -> {
         var persistedRules = response.getRulesByKey().values().stream()
           .map(ruleDefinitionDto -> new RulesTreeNode.Rule(ruleDefinitionDto,
@@ -256,7 +257,7 @@ private void recomputeDirtyState() {
       .exceptionally(error -> {
         GlobalLogOutput.get().log("Could not recompute rules: " + error.getMessage(), ClientLogOutput.Level.ERROR);
         return null;
-      });
+      }));
   }
 
   @Override
@@ -270,22 +271,21 @@ public void save(SonarLintGlobalSettings settings) {
         return rule;
       }));
     settings.setRulesByKey(nonDefaultRulesConfigurationByKey);
-    getService(BackendService.class).updateStandaloneRulesConfiguration(nonDefaultRulesConfigurationByKey);
+    runOnPooledThread(project, () -> getService(BackendService.class).updateStandaloneRulesConfiguration(nonDefaultRulesConfigurationByKey));
   }
 
   @Override
   public void load(SonarLintGlobalSettings settings) {
     panel.startLoading();
     selectedRuleKey = null;
-    getService(BackendService.class).getListAllStandaloneRulesDefinitions()
+    runOnPooledThread(project, () -> getService(BackendService.class).getListAllStandaloneRulesDefinitions()
       .thenAcceptAsync(response -> {
         allRulesStateByKey.clear();
-
-        var ruleNodes = response.getRulesByKey().values().stream()
-          .map(ruleDefinitionDto -> new RulesTreeNode.Rule(ruleDefinitionDto,
-            loadRuleActivation(settings, ruleDefinitionDto),
-            loadNonDefaultRuleParams(settings, ruleDefinitionDto)))
-          .collect(Collectors.toMap(RulesTreeNode.Rule::getKey, r -> r));
+          var ruleNodes = response.getRulesByKey().values().stream()
+            .map(ruleDefinitionDto -> new RulesTreeNode.Rule(ruleDefinitionDto,
+              loadRuleActivation(settings, ruleDefinitionDto),
+              loadNonDefaultRuleParams(settings, ruleDefinitionDto)))
+            .collect(Collectors.toMap(RulesTreeNode.Rule::getKey, r -> r));
 
         allRulesStateByKey.putAll(ruleNodes);
 
@@ -299,7 +299,7 @@ public void load(SonarLintGlobalSettings settings) {
       .exceptionally(error -> {
         GlobalLogOutput.get().log("Could not load rules: " + error.getMessage(), ClientLogOutput.Level.ERROR);
         return null;
-      });
+      }));
   }
 
   private void restoreDefaults() {
@@ -507,42 +507,47 @@ private void updateParamsAndDescriptionPanel() {
   }
 
   private void updateParamsAndDescriptionPanel(RulesTreeNode.Rule singleNode) {
-    ruleHeaderPanel.updateForRuleConfiguration(singleNode.getKey(), singleNode.type(), singleNode.severity(), singleNode.attribute(), singleNode.impacts());
+    ruleHeaderPanel.updateForRuleConfiguration(singleNode.getKey(), singleNode.type(), singleNode.severity(), singleNode.attribute(),
+      singleNode.impacts());
     var fileType = RuleLanguages.Companion.findFileTypeByRuleLanguage(singleNode.language());
 
-    getService(BackendService.class).getStandaloneRuleDetails(new GetStandaloneRuleDescriptionParams(singleNode.getKey()))
-      .thenAcceptAsync(details -> runOnUiThread(project, ModalityState.stateForComponent(getComponent()), () -> {
-        details.getDescription().map(
-          monolithDescription -> {
-            ruleDescription.addMonolith(monolithDescription, fileType);
-            return null;
-          },
-          withSections -> {
-            ruleDescription.addSections(withSections, fileType);
-            return null;
-          });
-
-        myParamsPanel.removeAll();
-        final var configPanelAnchor = new JBPanel<>(new GridLayout());
-        setConfigPanel(configPanelAnchor, singleNode, details.getRuleDefinition().getParamsByKey());
-        if (configPanelAnchor.getComponentCount() != 0) {
-          rulesParamsSeparator = new RulesParamsSeparator();
-          myParamsPanel.add(rulesParamsSeparator,
-            new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
-              JBUI.emptyInsets(), 0, 0));
-          myParamsPanel.add(configPanelAnchor, new GridBagConstraints(0, 1, 1, 1, 1.0, 1.0, GridBagConstraints.WEST, GridBagConstraints.BOTH,
-            JBUI.insetsLeft(2), 0, 0));
-        } else {
-          myParamsPanel.add(configPanelAnchor, new GridBagConstraints(0, 0, 1, 1, 1.0, 1.0, GridBagConstraints.WEST, GridBagConstraints.BOTH,
-            JBUI.insetsLeft(2), 0, 0));
-        }
-        myParamsPanel.revalidate();
-        myParamsPanel.repaint();
-      }))
-      .exceptionally(error -> {
-        GlobalLogOutput.get().log("Could not retrieve rule description", ClientLogOutput.Level.ERROR);
-        return null;
-      });
+    runOnPooledThread(project, () ->
+      getService(BackendService.class).getStandaloneRuleDetails(new GetStandaloneRuleDescriptionParams(singleNode.getKey()))
+        .thenAcceptAsync(details -> runOnUiThread(project, ModalityState.stateForComponent(getComponent()), () -> {
+          details.getDescription().map(
+            monolithDescription -> {
+              ruleDescription.addMonolith(monolithDescription, fileType);
+              return null;
+            },
+            withSections -> {
+              ruleDescription.addSections(withSections, fileType);
+              return null;
+            });
+
+          myParamsPanel.removeAll();
+          final var configPanelAnchor = new JBPanel<>(new GridLayout());
+          setConfigPanel(configPanelAnchor, singleNode, details.getRuleDefinition().getParamsByKey());
+          if (configPanelAnchor.getComponentCount() != 0) {
+            rulesParamsSeparator = new RulesParamsSeparator();
+            myParamsPanel.add(rulesParamsSeparator,
+              new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
+                JBUI.emptyInsets(), 0, 0));
+            myParamsPanel.add(configPanelAnchor, new GridBagConstraints(0, 1, 1, 1, 1.0, 1.0, GridBagConstraints.WEST,
+              GridBagConstraints.BOTH,
+              JBUI.insetsLeft(2), 0, 0));
+          } else {
+            myParamsPanel.add(configPanelAnchor, new GridBagConstraints(0, 0, 1, 1, 1.0, 1.0, GridBagConstraints.WEST,
+              GridBagConstraints.BOTH,
+              JBUI.insetsLeft(2), 0, 0));
+          }
+          myParamsPanel.revalidate();
+          myParamsPanel.repaint();
+        }))
+        .exceptionally(error -> {
+          GlobalLogOutput.get().log("Could not retrieve rule description", ClientLogOutput.Level.ERROR);
+          return null;
+        })
+    );
   }
 
   private void setConfigPanel(final JPanel configPanelAnchor, RulesTreeNode.Rule rule, Map<String, RuleParamDefinitionDto> paramsByKey) {
@@ -663,6 +668,7 @@ public void textChanged(DocumentEvent e) {
           rule.getCustomParams().remove(param.getKey());
         }
         recomputeDirtyState();
+
         rulesParamsSeparator.updateDefaultLinkVisibility();
       }
     });
diff --git a/src/main/java/org/sonarlint/intellij/config/global/wizard/AuthStep.java b/src/main/java/org/sonarlint/intellij/config/global/wizard/AuthStep.java
index 2a6181b3b5..2a1b88c1a5 100644
--- a/src/main/java/org/sonarlint/intellij/config/global/wizard/AuthStep.java
+++ b/src/main/java/org/sonarlint/intellij/config/global/wizard/AuthStep.java
@@ -234,6 +234,7 @@ private void checkConnection() throws CommitStepException {
     ConnectionTestTask test = new ConnectionTestTask(tmpServer);
     var msg = "Failed to connect to the server. Please check the configuration.";
     ValidateConnectionResponse result;
+
     try {
       result = ProgressManager.getInstance().run(test);
     } catch (Exception e) {
diff --git a/src/main/java/org/sonarlint/intellij/config/project/SonarLintProjectConfigurable.java b/src/main/java/org/sonarlint/intellij/config/project/SonarLintProjectConfigurable.java
index 026f17b2f6..8e2838c053 100644
--- a/src/main/java/org/sonarlint/intellij/config/project/SonarLintProjectConfigurable.java
+++ b/src/main/java/org/sonarlint/intellij/config/project/SonarLintProjectConfigurable.java
@@ -45,6 +45,7 @@
 import static org.sonarlint.intellij.config.Settings.getGlobalSettings;
 import static org.sonarlint.intellij.config.Settings.getSettingsFor;
 import static org.sonarlint.intellij.util.ThreadUtilsKt.computeOnPooledThread;
+import static org.sonarlint.intellij.util.ThreadUtilsKt.runOnPooledThread;
 
 /**
  * Coordinates creation of models and visual components from persisted settings.
@@ -96,11 +97,13 @@ public void apply() throws ConfigurationException {
       var projectSettings = getSettingsFor(project);
       boolean exclusionsModified = panel.areExclusionsModified(projectSettings);
       panel.save(project, projectSettings);
-      project.getMessageBus().syncPublisher(ProjectConfigurationListener.TOPIC).changed(projectSettings);
+      runOnPooledThread(project, () -> {
+        project.getMessageBus().syncPublisher(ProjectConfigurationListener.TOPIC).changed(projectSettings);
 
-      if (exclusionsModified) {
-        getService(project, AnalysisSubmitter.class).autoAnalyzeOpenFiles(TriggerType.CONFIG_CHANGE);
-      }
+        if (exclusionsModified) {
+          getService(project, AnalysisSubmitter.class).autoAnalyzeOpenFiles(TriggerType.CONFIG_CHANGE);
+        }
+      });
     }
   }
 
diff --git a/src/main/java/org/sonarlint/intellij/core/BackendService.kt b/src/main/java/org/sonarlint/intellij/core/BackendService.kt
index b20891d382..378e433195 100644
--- a/src/main/java/org/sonarlint/intellij/core/BackendService.kt
+++ b/src/main/java/org/sonarlint/intellij/core/BackendService.kt
@@ -179,22 +179,24 @@ class BackendService : Disposable {
         ApplicationManager.getApplication().messageBus.connect()
             .subscribe(GlobalConfigurationListener.TOPIC, object : GlobalConfigurationListener.Adapter() {
                 override fun applied(previousSettings: SonarLintGlobalSettings, newSettings: SonarLintGlobalSettings) {
-                    connectionsUpdated(newSettings.serverConnections)
-                    val changedConnections = newSettings.serverConnections.filter { connection ->
-                        val previousConnection = previousSettings.getServerConnectionByName(connection.name)
-                        previousConnection.isPresent && !connection.hasSameCredentials(previousConnection.get())
+                    runOnPooledThread {
+                        connectionsUpdated(newSettings.serverConnections)
+                        val changedConnections = newSettings.serverConnections.filter { connection ->
+                            val previousConnection = previousSettings.getServerConnectionByName(connection.name)
+                            previousConnection.isPresent && !connection.hasSameCredentials(previousConnection.get())
+                        }
+                        credentialsChanged(changedConnections)
                     }
-                    credentialsChanged(changedConnections)
                 }
 
                 override fun changed(serverList: List<ServerConnection>) {
-                    connectionsUpdated(serverList)
+                    runOnPooledThread { connectionsUpdated(serverList) }
                 }
             })
         ApplicationManager.getApplication().messageBus.connect()
             .subscribe(ProjectManager.TOPIC, object : ProjectManagerListener {
                 override fun projectClosing(project: Project) {
-                    this@BackendService.projectClosed(project)
+                    runOnPooledThread { this@BackendService.projectClosed(project) }
                 }
             })
 
@@ -280,9 +282,7 @@ class BackendService : Disposable {
 
     private fun handleSloopExited() {
         ProjectManager.getInstance().openProjects.forEach { project ->
-            runOnUiThread(project) {
-                getService(project, SonarLintToolWindow::class.java).refreshViews()
-            }
+            getService(project, SonarLintToolWindow::class.java).refreshViews()
         }
         projectLessNotification(
             null,
@@ -505,7 +505,9 @@ class BackendService : Disposable {
                 )
             )
         }
-        refreshTaintVulnerabilities(project)
+        runOnPooledThread {
+            refreshTaintVulnerabilities(project)
+        }
     }
 
     internal fun projectClosed(project: Project) {
@@ -518,31 +520,32 @@ class BackendService : Disposable {
         ConfigurationScopeDto(projectId(project), null, true, project.name,
             BindingConfigurationDto(binding?.connectionName, binding?.projectKey, areBindingSuggestionsDisabledFor(project)))
 
-
     fun projectBound(project: Project, newBinding: ProjectBinding) {
-        notifyBackend {
-            it.configurationService.didUpdateBinding(
-                DidUpdateBindingParams(
-                    projectId(project), BindingConfigurationDto(
-                    newBinding.connectionName, newBinding.projectKey, areBindingSuggestionsDisabledFor(project)
-                )
-                )
-            )
-        }
-        newBinding.moduleBindingsOverrides.forEach { (module, projectKey) ->
-            val moduleId = moduleId(module)
+        runOnPooledThread(project) {
             notifyBackend {
                 it.configurationService.didUpdateBinding(
                     DidUpdateBindingParams(
-                        moduleId, BindingConfigurationDto(
-                        // we don't want binding suggestions for modules
-                        newBinding.connectionName, projectKey, true
-                    )
+                        projectId(project), BindingConfigurationDto(
+                            newBinding.connectionName, newBinding.projectKey, areBindingSuggestionsDisabledFor(project)
+                        )
                     )
                 )
             }
+            newBinding.moduleBindingsOverrides.forEach { (module, projectKey) ->
+                val moduleId = moduleId(module)
+                notifyBackend {
+                    it.configurationService.didUpdateBinding(
+                        DidUpdateBindingParams(
+                            moduleId, BindingConfigurationDto(
+                                // we don't want binding suggestions for modules
+                                newBinding.connectionName, projectKey, true
+                            )
+                        )
+                    )
+                }
+            }
+            refreshTaintVulnerabilities(project)
         }
-        refreshTaintVulnerabilities(project)
     }
 
     fun projectUnbound(project: Project) {
@@ -553,7 +556,9 @@ class BackendService : Disposable {
                 )
             )
         }
-        refreshTaintVulnerabilities(project)
+        runOnPooledThread {
+            refreshTaintVulnerabilities(project)
+        }
     }
 
     fun modulesAdded(project: Project, modules: List<Module>) {
@@ -773,20 +778,20 @@ class BackendService : Disposable {
     }
 
     fun restartBackendService() {
-        if (isAlive()) {
-            return
+        runOnPooledThread {
+            if (isAlive()) {
+                return@runOnPooledThread
+            }
+            initializationTriedOnce.set(false)
+            backendFuture = CompletableFuture()
+            sloop = null
+            ensureBackendInitialized().thenAcceptAsync { catchUpWithBackend(it) }
         }
-        initializationTriedOnce.set(false)
-        backendFuture = CompletableFuture()
-        sloop = null
-        ensureBackendInitialized().thenAcceptAsync { catchUpWithBackend(it) }
     }
 
     private fun catchUpWithBackend(rpcServer: SonarLintRpcServer) {
         ProjectManager.getInstance().openProjects.forEach { project ->
-            runOnUiThread(project) {
-                getService(project, SonarLintToolWindow::class.java).refreshViews()
-            }
+            getService(project, SonarLintToolWindow::class.java).refreshViews()
 
             val binding = getService(project, ProjectBindingManager::class.java).binding
             rpcServer.configurationService.didAddConfigurationScopes(
diff --git a/src/main/java/org/sonarlint/intellij/core/ProjectBindingManager.java b/src/main/java/org/sonarlint/intellij/core/ProjectBindingManager.java
index 6c3b6e09e4..3db43821cd 100644
--- a/src/main/java/org/sonarlint/intellij/core/ProjectBindingManager.java
+++ b/src/main/java/org/sonarlint/intellij/core/ProjectBindingManager.java
@@ -158,8 +158,10 @@ public void unbind() {
 
     SonarLintProjectNotifications.Companion.get(myProject).reset();
     if (previousBinding != null) {
-      myProject.getMessageBus().syncPublisher(ProjectBindingListenerKt.getPROJECT_BINDING_TOPIC()).bindingChanged();
-      getService(BackendService.class).projectUnbound(myProject);
+      runOnPooledThread(myProject, () -> {
+        myProject.getMessageBus().syncPublisher(ProjectBindingListenerKt.getPROJECT_BINDING_TOPIC()).bindingChanged();
+        getService(BackendService.class).projectUnbound(myProject);
+      });
     }
   }
 
diff --git a/src/main/java/org/sonarlint/intellij/editor/CodeAnalyzerRestarter.kt b/src/main/java/org/sonarlint/intellij/editor/CodeAnalyzerRestarter.kt
index 5d0eba14aa..8c7330291c 100644
--- a/src/main/java/org/sonarlint/intellij/editor/CodeAnalyzerRestarter.kt
+++ b/src/main/java/org/sonarlint/intellij/editor/CodeAnalyzerRestarter.kt
@@ -20,6 +20,7 @@
 package org.sonarlint.intellij.editor
 
 import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer
+import com.intellij.openapi.application.ApplicationManager
 import com.intellij.openapi.components.Service
 import com.intellij.openapi.fileEditor.FileEditorManager
 import com.intellij.openapi.project.Project
@@ -55,6 +56,7 @@ class CodeAnalyzerRestarter @NonInjectable internal constructor(private val myPr
         if (!virtualFile.isValid) {
             return null
         }
+        ApplicationManager.getApplication().assertReadAccessAllowed()
         return PsiManager.getInstance(myProject).findFile(virtualFile)
     }
 }
diff --git a/src/main/java/org/sonarlint/intellij/editor/DisableRuleIntentionAction.java b/src/main/java/org/sonarlint/intellij/editor/DisableRuleIntentionAction.java
index 90aa3c0fbc..76b2e06ba0 100644
--- a/src/main/java/org/sonarlint/intellij/editor/DisableRuleIntentionAction.java
+++ b/src/main/java/org/sonarlint/intellij/editor/DisableRuleIntentionAction.java
@@ -30,13 +30,13 @@
 import org.jetbrains.annotations.Nls;
 import org.jetbrains.annotations.NotNull;
 import org.sonarlint.intellij.analysis.AnalysisSubmitter;
-import org.sonarlint.intellij.common.util.SonarLintUtils;
 import org.sonarlint.intellij.core.BackendService;
 import org.sonarlint.intellij.trigger.TriggerType;
 
 import static org.sonarlint.intellij.common.util.SonarLintUtils.getService;
 import static org.sonarlint.intellij.config.Settings.getGlobalSettings;
 import static org.sonarlint.intellij.config.Settings.getSettingsFor;
+import static org.sonarlint.intellij.util.ThreadUtilsKt.runOnPooledThread;
 
 public class DisableRuleIntentionAction implements IntentionAction, LowPriorityAction, Iconable {
   private final String ruleKey;
@@ -60,8 +60,10 @@ public class DisableRuleIntentionAction implements IntentionAction, LowPriorityA
   @Override public void invoke(@NotNull Project project, Editor editor, PsiFile file) {
     getGlobalSettings().disableRule(ruleKey);
     var rulesByKey = getGlobalSettings().getRulesByKey();
-    getService(BackendService.class).updateStandaloneRulesConfiguration(rulesByKey);
-    SonarLintUtils.getService(project, AnalysisSubmitter.class).autoAnalyzeOpenFiles(TriggerType.BINDING_UPDATE);
+    runOnPooledThread(project, () -> {
+      getService(BackendService.class).updateStandaloneRulesConfiguration(rulesByKey);
+      getService(project, AnalysisSubmitter.class).autoAnalyzeOpenFiles(TriggerType.BINDING_UPDATE);
+    });
   }
 
   @Override public boolean startInWriteAction() {
diff --git a/src/main/java/org/sonarlint/intellij/editor/EditorDecorator.kt b/src/main/java/org/sonarlint/intellij/editor/EditorDecorator.kt
index 4722d2305a..a2b4c59b50 100644
--- a/src/main/java/org/sonarlint/intellij/editor/EditorDecorator.kt
+++ b/src/main/java/org/sonarlint/intellij/editor/EditorDecorator.kt
@@ -33,14 +33,15 @@ import com.intellij.openapi.editor.markup.TextAttributes
 import com.intellij.openapi.project.Project
 import com.intellij.openapi.util.Disposer
 import com.intellij.ui.JBColor
-import java.awt.Font
-import java.util.function.Consumer
 import org.sonarlint.intellij.common.ui.ReadActionUtils.Companion.computeReadActionSafely
 import org.sonarlint.intellij.config.SonarLintTextAttributes
 import org.sonarlint.intellij.finding.Flow
 import org.sonarlint.intellij.finding.LiveFinding
 import org.sonarlint.intellij.finding.Location
 import org.sonarlint.intellij.finding.issue.vulnerabilities.LocalTaintVulnerability
+import org.sonarlint.intellij.ui.UiUtils.Companion.runOnUiThread
+import java.awt.Font
+import java.util.function.Consumer
 
 private const val HIGHLIGHT_GROUP_ID = 1001
 
@@ -50,12 +51,22 @@ class EditorDecorator(private val project: Project) {
     private var blinker: RangeBlinker? = null
 
     fun removeHighlights() {
-        currentHighlightedDoc.forEach {
-            clearSecondaryLocationNumbers(it)
-            UpdateHighlightersUtil.setHighlightersToEditor(project, it, 0, it.textLength, emptyList(), null, HIGHLIGHT_GROUP_ID)
+        runOnUiThread(project) {
+            currentHighlightedDoc.forEach {
+                clearSecondaryLocationNumbers(it)
+                UpdateHighlightersUtil.setHighlightersToEditor(
+                    project,
+                    it,
+                    0,
+                    it.textLength,
+                    emptyList(),
+                    null,
+                    HIGHLIGHT_GROUP_ID
+                )
+            }
+            currentHighlightedDoc.clear()
+            stopBlinking()
         }
-        currentHighlightedDoc.clear()
-        stopBlinking()
     }
 
     private fun stopBlinking() {
diff --git a/src/main/java/org/sonarlint/intellij/editor/ShowLocationsIntentionAction.java b/src/main/java/org/sonarlint/intellij/editor/ShowLocationsIntentionAction.java
index e655e75385..6db2dfa5ac 100644
--- a/src/main/java/org/sonarlint/intellij/editor/ShowLocationsIntentionAction.java
+++ b/src/main/java/org/sonarlint/intellij/editor/ShowLocationsIntentionAction.java
@@ -34,8 +34,6 @@
 import org.sonarlint.intellij.finding.FindingContext;
 import org.sonarlint.intellij.finding.LiveFinding;
 
-import static org.sonarlint.intellij.ui.UiUtils.runOnUiThread;
-
 public class ShowLocationsIntentionAction implements IntentionAction, PriorityAction, Iconable {
   private final LiveFinding finding;
   private final FindingContext context;
@@ -60,7 +58,7 @@ public ShowLocationsIntentionAction(LiveFinding finding, FindingContext context)
   @Override public void invoke(@NotNull Project project, Editor editor, PsiFile file) {
     SonarLintUtils.getService(project, EditorDecorator.class).highlightFinding(finding);
     var sonarLintToolWindow = SonarLintUtils.getService(project, SonarLintToolWindow.class);
-    runOnUiThread(project, () -> sonarLintToolWindow.showFindingLocations(finding));
+    sonarLintToolWindow.showFindingLocations(finding);
   }
 
   @Override public boolean startInWriteAction() {
diff --git a/src/main/java/org/sonarlint/intellij/fix/ShowFixSuggestion.kt b/src/main/java/org/sonarlint/intellij/fix/ShowFixSuggestion.kt
index 2dfe16ddc0..4e1b48d157 100644
--- a/src/main/java/org/sonarlint/intellij/fix/ShowFixSuggestion.kt
+++ b/src/main/java/org/sonarlint/intellij/fix/ShowFixSuggestion.kt
@@ -41,14 +41,12 @@ class ShowFixSuggestion(private val project: Project, private val file: VirtualF
 
     fun show() {
         val fileEditorManager = FileEditorManager.getInstance(project)
-        val psiFile = PsiManager.getInstance(project).findFile(file) ?: return
+        val psiFile = computeReadActionSafely(project) { PsiManager.getInstance(project).findFile(file) } ?: return
         val document = computeReadActionSafely(project) { file.getDocument() } ?: return
 
         if (!isWithinBounds(document)) {
             get(project).simpleNotification(
-                null,
-                "Unable to open the fix suggestion, your file has probably changed",
-                NotificationType.WARNING
+                null, "Unable to open the fix suggestion, your file has probably changed", NotificationType.WARNING
             )
             return
         }
diff --git a/src/main/java/org/sonarlint/intellij/module/ModuleChangeListener.kt b/src/main/java/org/sonarlint/intellij/module/ModuleChangeListener.kt
index cdc2eb4e40..96849d577c 100644
--- a/src/main/java/org/sonarlint/intellij/module/ModuleChangeListener.kt
+++ b/src/main/java/org/sonarlint/intellij/module/ModuleChangeListener.kt
@@ -26,15 +26,16 @@ import com.intellij.util.Function
 import org.sonarlint.intellij.common.util.SonarLintUtils.getService
 import org.sonarlint.intellij.config.Settings.getSettingsFor
 import org.sonarlint.intellij.core.BackendService
+import org.sonarlint.intellij.util.runOnPooledThread
 
 class ModuleChangeListener(val project: Project) : ModuleListener {
 
     override fun modulesAdded(project: Project, modules: List<Module>) {
-        getService(BackendService::class.java).modulesAdded(project, modules)
+        runOnPooledThread(project) { getService(BackendService::class.java).modulesAdded(project, modules) }
     }
 
     override fun moduleRemoved(project: Project, module: Module) {
-        getService(BackendService::class.java).moduleRemoved(module)
+        runOnPooledThread(project) { getService(BackendService::class.java).moduleRemoved(module) }
     }
 
     override fun modulesRenamed(project: Project, modules: MutableList<out Module>, oldNameProvider: Function<in Module, String>) {
diff --git a/src/main/java/org/sonarlint/intellij/sharing/ConfigurationSharing.kt b/src/main/java/org/sonarlint/intellij/sharing/ConfigurationSharing.kt
index 27d3b74b2d..103a195abc 100644
--- a/src/main/java/org/sonarlint/intellij/sharing/ConfigurationSharing.kt
+++ b/src/main/java/org/sonarlint/intellij/sharing/ConfigurationSharing.kt
@@ -39,6 +39,7 @@ import org.sonarlint.intellij.documentation.SonarLintDocumentation
 import org.sonarlint.intellij.notifications.SonarLintProjectNotifications.Companion.get
 import org.sonarlint.intellij.sharing.SonarLintSharedFolderUtils.Companion.findSharedFolder
 import org.sonarlint.intellij.ui.UiUtils.Companion.runOnUiThread
+import org.sonarlint.intellij.util.runOnPooledThread
 import org.sonarsource.sonarlint.core.rpc.protocol.backend.binding.GetSharedConnectedModeConfigFileResponse
 import org.sonarsource.sonarlint.core.rpc.protocol.client.connection.ConnectionSuggestionDto
 
@@ -50,7 +51,7 @@ class ConfigurationSharing {
             if (project == null || project.isDisposed) return
 
             if (confirm(project)) {
-                createFile(project, modalityState)
+                runOnPooledThread(project) { createFile(project, modalityState) }
             }
         }
 
diff --git a/src/main/java/org/sonarlint/intellij/tasks/GetOrganizationTask.java b/src/main/java/org/sonarlint/intellij/tasks/GetOrganizationTask.java
index 9e94502fad..059daab3f1 100644
--- a/src/main/java/org/sonarlint/intellij/tasks/GetOrganizationTask.java
+++ b/src/main/java/org/sonarlint/intellij/tasks/GetOrganizationTask.java
@@ -29,6 +29,7 @@
 import org.sonarsource.sonarlint.core.rpc.protocol.backend.connection.org.OrganizationDto;
 
 import static org.sonarlint.intellij.util.ProgressUtils.waitForFuture;
+import static org.sonarlint.intellij.util.ThreadUtilsKt.computeOnPooledThread;
 
 public class GetOrganizationTask extends Task.Modal {
   private final ServerConnection server;
@@ -50,7 +51,8 @@ public void run(@NotNull ProgressIndicator indicator) {
 
     try {
       indicator.setText("Searching organization");
-      organization = waitForFuture(indicator, SonarLintUtils.getService(BackendService.class).getOrganization(server, organizationKey)).getOrganization();
+      organization = computeOnPooledThread("Get User Organizations", () ->
+        waitForFuture(indicator, SonarLintUtils.getService(BackendService.class).getOrganization(server, organizationKey)).getOrganization());
     } catch (Exception e) {
       SonarLintConsole.get(myProject).error("Failed to fetch organizations", e);
       exception = e;
diff --git a/src/main/java/org/sonarlint/intellij/tasks/GetOrganizationsTask.java b/src/main/java/org/sonarlint/intellij/tasks/GetOrganizationsTask.java
index d375335e7d..2ef2f9304a 100644
--- a/src/main/java/org/sonarlint/intellij/tasks/GetOrganizationsTask.java
+++ b/src/main/java/org/sonarlint/intellij/tasks/GetOrganizationsTask.java
@@ -30,6 +30,7 @@
 import org.sonarsource.sonarlint.core.rpc.protocol.backend.connection.org.OrganizationDto;
 
 import static org.sonarlint.intellij.util.ProgressUtils.waitForFuture;
+import static org.sonarlint.intellij.util.ThreadUtilsKt.computeOnPooledThread;
 
 /**
  * Only useful for SonarCloud
@@ -50,7 +51,8 @@ public void run(@NotNull ProgressIndicator indicator) {
     indicator.setIndeterminate(false);
 
     try {
-      organizations = waitForFuture(indicator, SonarLintUtils.getService(BackendService.class).listUserOrganizations(connection)).getUserOrganizations();
+      organizations = computeOnPooledThread("Get User Organizations",() ->
+        waitForFuture(indicator, SonarLintUtils.getService(BackendService.class).listUserOrganizations(connection)).getUserOrganizations());
     } catch (Exception e) {
       SonarLintConsole.get(myProject).error("Failed to fetch organizations", e);
       exception = e;
diff --git a/src/main/java/org/sonarlint/intellij/telemetry/SonarLintTelemetryImpl.kt b/src/main/java/org/sonarlint/intellij/telemetry/SonarLintTelemetryImpl.kt
index d42de07493..2b1a8044c7 100644
--- a/src/main/java/org/sonarlint/intellij/telemetry/SonarLintTelemetryImpl.kt
+++ b/src/main/java/org/sonarlint/intellij/telemetry/SonarLintTelemetryImpl.kt
@@ -22,6 +22,7 @@ package org.sonarlint.intellij.telemetry
 import java.util.concurrent.CompletableFuture
 import org.sonarlint.intellij.common.util.SonarLintUtils.getService
 import org.sonarlint.intellij.core.BackendService
+import org.sonarlint.intellij.util.runOnPooledThread
 import org.sonarsource.sonarlint.core.rpc.protocol.backend.telemetry.TelemetryRpcService
 import org.sonarsource.sonarlint.core.rpc.protocol.client.telemetry.AddQuickFixAppliedForRuleParams
 import org.sonarsource.sonarlint.core.rpc.protocol.client.telemetry.DevNotificationsClickedParams
@@ -80,7 +81,7 @@ class SonarLintTelemetryImpl : SonarLintTelemetry {
 
     companion object {
         private fun notifyTelemetry(action: (TelemetryRpcService) -> Unit) {
-            getService(BackendService::class.java).notifyTelemetry(action)
+            runOnPooledThread { getService(BackendService::class.java).notifyTelemetry(action) }
         }
     }
 }
diff --git a/src/main/java/org/sonarlint/intellij/trigger/BuildFinishedAnalysisTrigger.java b/src/main/java/org/sonarlint/intellij/trigger/BuildFinishedAnalysisTrigger.java
index 2916dbc937..90435f0564 100644
--- a/src/main/java/org/sonarlint/intellij/trigger/BuildFinishedAnalysisTrigger.java
+++ b/src/main/java/org/sonarlint/intellij/trigger/BuildFinishedAnalysisTrigger.java
@@ -24,7 +24,9 @@
 import java.util.UUID;
 import org.sonarlint.intellij.analysis.AnalysisSubmitter;
 import org.sonarlint.intellij.common.ui.SonarLintConsole;
-import org.sonarlint.intellij.common.util.SonarLintUtils;
+
+import static org.sonarlint.intellij.common.util.SonarLintUtils.getService;
+import static org.sonarlint.intellij.util.ThreadUtilsKt.runOnPooledThread;
 
 public class BuildFinishedAnalysisTrigger implements BuildManagerListener {
 
@@ -34,7 +36,9 @@ public class BuildFinishedAnalysisTrigger implements BuildManagerListener {
       return;
     }
 
-    SonarLintUtils.getService(project, SonarLintConsole.class).debug("build finished");
-    SonarLintUtils.getService(project, AnalysisSubmitter.class).autoAnalyzeOpenFiles(TriggerType.COMPILATION);
+    runOnPooledThread(project, () -> {
+      getService(project, SonarLintConsole.class).debug("build finished");
+      getService(project, AnalysisSubmitter.class).autoAnalyzeOpenFiles(TriggerType.COMPILATION);
+    });
   }
 }
diff --git a/src/main/java/org/sonarlint/intellij/trigger/CompilationFinishedAnalysisTrigger.java b/src/main/java/org/sonarlint/intellij/trigger/CompilationFinishedAnalysisTrigger.java
index 10dcd57b18..4eb0ff2787 100644
--- a/src/main/java/org/sonarlint/intellij/trigger/CompilationFinishedAnalysisTrigger.java
+++ b/src/main/java/org/sonarlint/intellij/trigger/CompilationFinishedAnalysisTrigger.java
@@ -23,7 +23,9 @@
 import com.intellij.openapi.compiler.CompileContext;
 import org.sonarlint.intellij.analysis.AnalysisSubmitter;
 import org.sonarlint.intellij.common.ui.SonarLintConsole;
-import org.sonarlint.intellij.common.util.SonarLintUtils;
+
+import static org.sonarlint.intellij.common.util.SonarLintUtils.getService;
+import static org.sonarlint.intellij.util.ThreadUtilsKt.runOnPooledThread;
 
 public class CompilationFinishedAnalysisTrigger implements CompilationStatusListener {
 
@@ -34,7 +36,7 @@ public class CompilationFinishedAnalysisTrigger implements CompilationStatusList
   @Override
   public void compilationFinished(boolean aborted, int errors, int warnings, CompileContext compileContext) {
     var compiledProject = compileContext.getProject();
-    SonarLintUtils.getService(compiledProject, SonarLintConsole.class).debug("compilation finished");
-    SonarLintUtils.getService(compiledProject, AnalysisSubmitter.class).autoAnalyzeOpenFiles(TriggerType.COMPILATION);
+    getService(compiledProject, SonarLintConsole.class).debug("compilation finished");
+    runOnPooledThread(compiledProject, () -> getService(compiledProject, AnalysisSubmitter.class).autoAnalyzeOpenFiles(TriggerType.COMPILATION));
   }
 }
diff --git a/src/main/java/org/sonarlint/intellij/trigger/EditorChangeTrigger.java b/src/main/java/org/sonarlint/intellij/trigger/EditorChangeTrigger.java
index c7e34f95de..4364f2535d 100644
--- a/src/main/java/org/sonarlint/intellij/trigger/EditorChangeTrigger.java
+++ b/src/main/java/org/sonarlint/intellij/trigger/EditorChangeTrigger.java
@@ -36,11 +36,12 @@
 import javax.annotation.concurrent.ThreadSafe;
 import org.sonarlint.intellij.analysis.AnalysisSubmitter;
 import org.sonarlint.intellij.analysis.Cancelable;
-import org.sonarlint.intellij.common.util.SonarLintUtils;
 import org.sonarlint.intellij.messages.AnalysisListener;
 import org.sonarlint.intellij.util.SonarLintAppUtils;
 
+import static org.sonarlint.intellij.common.util.SonarLintUtils.getService;
 import static org.sonarlint.intellij.config.Settings.getGlobalSettings;
+import static org.sonarlint.intellij.util.ThreadUtilsKt.runOnPooledThread;
 
 @ThreadSafe
 @Service(Service.Level.PROJECT)
@@ -141,7 +142,7 @@ private void triggerFiles(List<VirtualFile> files) {
             return;
           }
           files.forEach(eventMap::remove);
-          task = SonarLintUtils.getService(myProject, AnalysisSubmitter.class).autoAnalyzeFiles(openFilesToAnalyze, TriggerType.EDITOR_CHANGE);
+          task = getService(myProject, AnalysisSubmitter.class).autoAnalyzeFiles(openFilesToAnalyze, TriggerType.EDITOR_CHANGE);
         }
       }
     }
@@ -164,7 +165,7 @@ private void checkTimers() {
           filesToTrigger.add(event.getKey());
         }
       }
-      triggerFiles(filesToTrigger);
+      runOnPooledThread(myProject, () -> triggerFiles(filesToTrigger));
     }
 
   }
diff --git a/src/main/java/org/sonarlint/intellij/trigger/EditorOpenTrigger.java b/src/main/java/org/sonarlint/intellij/trigger/EditorOpenTrigger.java
index d103e54379..da72314c00 100644
--- a/src/main/java/org/sonarlint/intellij/trigger/EditorOpenTrigger.java
+++ b/src/main/java/org/sonarlint/intellij/trigger/EditorOpenTrigger.java
@@ -26,11 +26,12 @@
 import org.sonarlint.intellij.analysis.AnalysisSubmitter;
 
 import static org.sonarlint.intellij.common.util.SonarLintUtils.getService;
+import static org.sonarlint.intellij.util.ThreadUtilsKt.runOnPooledThread;
 
 public class EditorOpenTrigger implements FileEditorManagerListener {
 
   @Override
   public void fileOpened(@NotNull FileEditorManager source, @NotNull VirtualFile file) {
-    getService(source.getProject(), AnalysisSubmitter.class).autoAnalyzeFile(file, TriggerType.EDITOR_OPEN);
+    runOnPooledThread(source.getProject(), () -> getService(source.getProject(), AnalysisSubmitter.class).autoAnalyzeFile(file, TriggerType.EDITOR_OPEN));
   }
 }
diff --git a/src/main/java/org/sonarlint/intellij/trigger/SonarLintCheckinHandler.java b/src/main/java/org/sonarlint/intellij/trigger/SonarLintCheckinHandler.java
index 4d3ee22446..2050c477df 100644
--- a/src/main/java/org/sonarlint/intellij/trigger/SonarLintCheckinHandler.java
+++ b/src/main/java/org/sonarlint/intellij/trigger/SonarLintCheckinHandler.java
@@ -20,6 +20,7 @@
 package org.sonarlint.intellij.trigger;
 
 import com.intellij.ide.util.PropertiesComponent;
+import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.options.UnnamedConfigurable;
 import com.intellij.openapi.progress.ProgressIndicator;
@@ -64,6 +65,7 @@
 
 import static org.sonarlint.intellij.common.util.SonarLintUtils.getService;
 import static org.sonarlint.intellij.config.Settings.getGlobalSettings;
+import static org.sonarlint.intellij.ui.UiUtils.runOnUiThread;
 
 public class SonarLintCheckinHandler extends CheckinHandler {
   private static final Logger LOGGER = Logger.getInstance(SonarLintCheckinHandler.class);
@@ -95,7 +97,7 @@ public ReturnResult beforeCheckin(@Nullable CommitExecutor executor, PairConsume
     var affectedFiles = new HashSet<>(checkinPanel.getVirtualFiles());
     // this will block EDT (modal)
     try {
-      var analysisIdsByCallback = SonarLintUtils.getService(project, AnalysisSubmitter.class).analyzeFilesPreCommit(affectedFiles);
+      var analysisIdsByCallback = getService(project, AnalysisSubmitter.class).analyzeFilesPreCommit(affectedFiles);
       if (analysisIdsByCallback == null) {
         return ReturnResult.CANCEL;
       }
@@ -240,7 +242,11 @@ private ReturnResult showYesNoCancel(String resultStr) {
   }
 
   private void showChangedFilesTab(AnalysisResult analysisResult) {
-    SonarLintUtils.getService(project, SonarLintToolWindow.class).openReportTab(analysisResult);
+    if (ApplicationManager.getApplication().isUnitTestMode()) {
+      getService(project, SonarLintToolWindow.class).openReportTab(analysisResult);
+    } else {
+      runOnUiThread(project, () -> getService(project, SonarLintToolWindow.class).openReportTab(analysisResult));
+    }
   }
 
   private class MyRefreshableOnComponent implements RefreshableOnComponent, UnnamedConfigurable {
diff --git a/src/main/java/org/sonarlint/intellij/ui/AbstractIssuesPanel.java b/src/main/java/org/sonarlint/intellij/ui/AbstractIssuesPanel.java
index 964d2cc86d..2f769bdccf 100644
--- a/src/main/java/org/sonarlint/intellij/ui/AbstractIssuesPanel.java
+++ b/src/main/java/org/sonarlint/intellij/ui/AbstractIssuesPanel.java
@@ -66,7 +66,7 @@ public abstract class AbstractIssuesPanel extends SimpleToolWindowPanel implemen
   protected FindingDetailsPanel findingDetailsPanel;
 
   protected AbstractIssuesPanel(Project project) {
-    super(false, true);
+    super(false, false);
     this.project = project;
 
     createIssuesTree();
@@ -242,8 +242,8 @@ public <T extends Finding> void updateOnSelect(@Nullable LiveFinding issue, Show
           return;
         }
 
-        runOnUiThread(project, () ->
-          findingDetailsPanel.showServerOnlyIssue(showFinding.getModule(), showFinding.getFile(), showFinding.getRuleKey(), rangeMarker, showFinding.getFlows(),
+        runOnUiThread(project,
+          () -> findingDetailsPanel.showServerOnlyIssue(showFinding.getModule(), showFinding.getFile(), showFinding.getRuleKey(), rangeMarker, showFinding.getFlows(),
             showFinding.getFlowMessage()));
       });
     }
diff --git a/src/main/java/org/sonarlint/intellij/ui/AutoTriggerStatusPanel.java b/src/main/java/org/sonarlint/intellij/ui/AutoTriggerStatusPanel.java
index 2834c7bed3..98c4914a86 100644
--- a/src/main/java/org/sonarlint/intellij/ui/AutoTriggerStatusPanel.java
+++ b/src/main/java/org/sonarlint/intellij/ui/AutoTriggerStatusPanel.java
@@ -72,8 +72,8 @@ public class AutoTriggerStatusPanel {
   public AutoTriggerStatusPanel(Project project) {
     this.project = project;
     createPanel();
-    switchCards();
-    CurrentFileStatusPanel.subscribeToEventsThatAffectCurrentFile(project, this::switchCards);
+    runOnUiThread(project, this::switchCards);
+    CurrentFileStatusPanel.subscribeToEventsThatAffectCurrentFile(project, () -> runOnUiThread(project, this::switchCards));
   }
 
   public JPanel getPanel() {
diff --git a/src/main/java/org/sonarlint/intellij/ui/CurrentFileConnectedModePanel.java b/src/main/java/org/sonarlint/intellij/ui/CurrentFileConnectedModePanel.java
index a5aa214521..c0ef36a8d4 100644
--- a/src/main/java/org/sonarlint/intellij/ui/CurrentFileConnectedModePanel.java
+++ b/src/main/java/org/sonarlint/intellij/ui/CurrentFileConnectedModePanel.java
@@ -21,7 +21,6 @@
 
 import com.intellij.ide.IdeTooltipManager;
 import com.intellij.openapi.actionSystem.ActionManager;
-import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.module.Module;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.wm.ToolWindowManager;
@@ -158,32 +157,32 @@ private <T> T illegalStateIfNull(@Nullable T checkForNull, String messageIfNull)
   }
 
   private void switchCards() {
-    ApplicationManager.getApplication().assertIsDispatchThread();
-
-    var selectedFile = SonarLintUtils.getSelectedFile(project);
-    if (selectedFile != null) {
-      // Checking connected mode state may take time, so lets move from EDT to pooled thread
-      runOnPooledThread(project, () -> {
-        var projectBindingManager = getService(project, ProjectBindingManager.class);
-        projectBindingManager.tryGetServerConnection().ifPresentOrElse(serverConnection -> {
-          try {
-            var module = SonarLintAppUtils.findModuleForFile(selectedFile, project);
-            if (module == null) {
-              switchCard(EMPTY);
-            } else {
-              connectedCard.updateTooltip(module, serverConnection);
-              switchCard(CONNECTED);
+    runOnUiThread(project, () -> {
+      var selectedFile = SonarLintUtils.getSelectedFile(project);
+      if (selectedFile != null) {
+        // Checking connected mode state may take time, so lets move from EDT to pooled thread
+        runOnPooledThread(project, () -> {
+          var projectBindingManager = getService(project, ProjectBindingManager.class);
+          projectBindingManager.tryGetServerConnection().ifPresentOrElse(serverConnection -> {
+            try {
+              var module = SonarLintAppUtils.findModuleForFile(selectedFile, project);
+              if (module == null) {
+                switchCard(EMPTY);
+              } else {
+                connectedCard.updateTooltip(module, serverConnection);
+                switchCard(CONNECTED);
+              }
+            } catch (IllegalStateException e) {
+              switchCard(ERROR);
             }
-          } catch (IllegalStateException e) {
-            switchCard(ERROR);
-          }
-        },
-          // No connection settings for project
-          () -> switchCard(NOT_CONNECTED));
-      });
-    } else {
-      switchCard(EMPTY);
-    }
+          },
+            // No connection settings for project
+            () -> switchCard(NOT_CONNECTED));
+        });
+      } else {
+        switchCard(EMPTY);
+      }
+    });
   }
 
   private void updateBranchTooltip() {
diff --git a/src/main/java/org/sonarlint/intellij/ui/CurrentFilePanel.java b/src/main/java/org/sonarlint/intellij/ui/CurrentFilePanel.java
index 77fb2b7773..d1ff95ed74 100644
--- a/src/main/java/org/sonarlint/intellij/ui/CurrentFilePanel.java
+++ b/src/main/java/org/sonarlint/intellij/ui/CurrentFilePanel.java
@@ -23,6 +23,7 @@
 import com.intellij.openapi.actionSystem.AnAction;
 import com.intellij.openapi.actionSystem.CommonDataKeys;
 import com.intellij.openapi.actionSystem.ex.ActionUtil;
+import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.ui.VerticalFlowLayout;
 import com.intellij.openapi.vfs.VirtualFile;
@@ -84,7 +85,7 @@ public CurrentFilePanel(Project project) {
     treePanel.add(tree);
     treePanel.add(oldTree);
 
-    treeScrollPane = ScrollPaneFactory.createScrollPane(treePanel);
+    treeScrollPane = ScrollPaneFactory.createScrollPane(treePanel, true);
 
     issuesPanel = new JBPanelWithEmptyText(new BorderLayout());
     var statusText = issuesPanel.getEmptyText();
@@ -204,6 +205,7 @@ private void updateIcon(@Nullable VirtualFile file, Collection<LiveIssue> issues
   }
 
   private static void doUpdateIcon(@Nullable VirtualFile file, Collection<LiveIssue> issues, ToolWindow toolWindow) {
+    ApplicationManager.getApplication().assertIsDispatchThread();
     boolean empty = file == null || issues.isEmpty();
     toolWindow.setIcon(empty ? SonarLintIcons.SONARLINT_TOOLWINDOW_EMPTY : SonarLintIcons.SONARLINT_TOOLWINDOW);
   }
@@ -222,17 +224,17 @@ public Object getData(@NonNls String dataId) {
     if (CommonDataKeys.VIRTUAL_FILE.is(dataId)) {
       return SonarLintUtils.getSelectedFile(project);
     }
-
+    
     return null;
   }
 
   public void refreshModel() {
     treeBuilder.refreshModel(project);
     oldTreeBuilder.refreshModel(project);
-    expandTree();
+    runOnUiThread(project, this::expandTree);
   }
 
   public void refreshView() {
-    update(currentFile, currentIssues);
+    runOnUiThread(project, () -> update(currentFile, currentIssues));
   }
 }
diff --git a/src/main/java/org/sonarlint/intellij/ui/ReportPanel.java b/src/main/java/org/sonarlint/intellij/ui/ReportPanel.java
index 11fa7accc7..dcc7f0d82d 100644
--- a/src/main/java/org/sonarlint/intellij/ui/ReportPanel.java
+++ b/src/main/java/org/sonarlint/intellij/ui/ReportPanel.java
@@ -72,6 +72,7 @@
 import static org.sonarlint.intellij.common.util.SonarLintUtils.getService;
 import static org.sonarlint.intellij.ui.SonarLintToolWindowFactory.createSplitter;
 import static org.sonarlint.intellij.ui.UiUtils.runOnUiThread;
+import static org.sonarlint.intellij.util.ThreadUtilsKt.runOnPooledThread;
 
 public class ReportPanel extends SimpleToolWindowPanel implements Disposable {
   private static final String SPLIT_PROPORTION_PROPERTY = "SONARLINT_ANALYSIS_RESULTS_SPLIT_PROPORTION";
@@ -95,7 +96,7 @@ public class ReportPanel extends SimpleToolWindowPanel implements Disposable {
   private JBPanelWithEmptyText findingsPanel;
 
   public ReportPanel(Project project) {
-    super(false, true);
+    super(false, false);
     this.project = project;
     lastAnalysisPanel = new LastAnalysisPanel();
     whatsNewPanel = new ReportTabStatusPanel(project);
@@ -143,15 +144,19 @@ public void updateFindings(AnalysisResult analysisResult) {
         .filter(e -> !e.getValue().isEmpty())
         .collect(Collectors.toMap(Map.Entry::getKey, e -> (Collection<LiveIssue>) e.getValue()));
 
-      treeBuilder.updateModel(newIssues);
-      oldTreeBuilder.updateModel(oldIssues);
-      securityHotspotTreeBuilder.updateModel(newHotspots);
-      oldSecurityHotspotTreeBuilder.updateModel(oldHotspots);
+      runOnUiThread(project, () -> {
+        treeBuilder.updateModel(newIssues);
+        oldTreeBuilder.updateModel(oldIssues);
+        securityHotspotTreeBuilder.updateModel(newHotspots);
+        oldSecurityHotspotTreeBuilder.updateModel(oldHotspots);
+      });
     } else {
-      securityHotspotTreeBuilder.updateModel(findings.getSecurityHotspotsPerFile());
-      oldSecurityHotspotTreeBuilder.updateModel(Collections.emptyMap());
-      treeBuilder.updateModel(findings.getIssuesPerFile());
-      oldTreeBuilder.updateModel(Collections.emptyMap());
+      runOnUiThread(project, () -> {
+        securityHotspotTreeBuilder.updateModel(findings.getSecurityHotspotsPerFile());
+        oldSecurityHotspotTreeBuilder.updateModel(Collections.emptyMap());
+        treeBuilder.updateModel(findings.getIssuesPerFile());
+        oldTreeBuilder.updateModel(Collections.emptyMap());
+      });
     }
 
     disableEmptyDisplay(true);
@@ -184,7 +189,7 @@ private void initPanel() {
     treePanel.add(securityHotspotTree);
     treePanel.add(oldTree);
     treePanel.add(oldSecurityHotspotTree);
-    findingsTreePane = ScrollPaneFactory.createScrollPane(treePanel);
+    findingsTreePane = ScrollPaneFactory.createScrollPane(treePanel, true);
     findingsPanel.add(findingsTreePane, BorderLayout.CENTER);
     findingsPanel.add(lastAnalysisPanel, BorderLayout.SOUTH);
     whatsNewPanel.add(lastAnalysisPanel, BorderLayout.WEST);
@@ -229,14 +234,16 @@ private void refreshToolbar() {
   }
 
   private void setTrees(boolean isFocusOnNewCode) {
-    tree.setShowsRootHandles(true);
-    oldTree.setShowsRootHandles(true);
-    securityHotspotTree.setShowsRootHandles(true);
-    oldSecurityHotspotTree.setShowsRootHandles(true);
-    tree.setVisible(true);
-    oldTree.setVisible(isFocusOnNewCode);
-    securityHotspotTree.setVisible(true);
-    oldSecurityHotspotTree.setVisible(isFocusOnNewCode);
+    runOnUiThread(project, () -> {
+      tree.setShowsRootHandles(true);
+      oldTree.setShowsRootHandles(true);
+      securityHotspotTree.setShowsRootHandles(true);
+      oldSecurityHotspotTree.setShowsRootHandles(true);
+      tree.setVisible(true);
+      oldTree.setVisible(isFocusOnNewCode);
+      securityHotspotTree.setVisible(true);
+      oldSecurityHotspotTree.setVisible(isFocusOnNewCode);
+    });
   }
 
   private void issueTreeSelectionChanged(TreeSelectionEvent e) {
@@ -408,22 +415,24 @@ public void clear() {
   }
 
   private void expandTree() {
-    if (treeBuilder.numberIssues() < 30) {
-      TreeUtil.expandAll(tree);
-    } else {
-      tree.expandRow(0);
-    }
+    runOnUiThread(project, () -> {
+      if (treeBuilder.numberIssues() < 30) {
+        TreeUtil.expandAll(tree);
+      } else {
+        tree.expandRow(0);
+      }
 
-    if (securityHotspotTreeBuilder.numberHotspots() < 30) {
-      TreeUtil.expandAll(securityHotspotTree);
-    } else {
-      securityHotspotTree.expandRow(0);
-    }
+      if (securityHotspotTreeBuilder.numberHotspots() < 30) {
+        TreeUtil.expandAll(securityHotspotTree);
+      } else {
+        securityHotspotTree.expandRow(0);
+      }
+    });
   }
 
   private void disableEmptyDisplay(Boolean state) {
     if (Boolean.FALSE.equals(state)) {
-      handleEmptyView();
+      runOnPooledThread(project, this::handleEmptyView);
     }
     findingsTreePane.setVisible(state);
     lastAnalysisPanel.setVisible(state);
diff --git a/src/main/java/org/sonarlint/intellij/ui/SecurityHotspotsPanel.java b/src/main/java/org/sonarlint/intellij/ui/SecurityHotspotsPanel.java
index b05b293335..8e5b89dcfa 100644
--- a/src/main/java/org/sonarlint/intellij/ui/SecurityHotspotsPanel.java
+++ b/src/main/java/org/sonarlint/intellij/ui/SecurityHotspotsPanel.java
@@ -101,7 +101,7 @@ public class SecurityHotspotsPanel extends SimpleToolWindowPanel implements Disp
   private FindingDetailsPanel findingDetailsPanel;
 
   public SecurityHotspotsPanel(Project project) {
-    super(false, true);
+    super(false, false);
     this.project = project;
     securityHotspotCount = 0;
     oldSecurityHotspotCount = 0;
@@ -124,7 +124,7 @@ private void initPanel() {
     findingDetailsPanel.setMinimumSize(new Dimension(350, 200));
     var findingsPanel = new JPanel(new BorderLayout());
     findingsPanel.add(createSplitter(project, this, this,
-      ScrollPaneFactory.createScrollPane(treePanel), findingDetailsPanel, SPLIT_PROPORTION_PROPERTY, 0.5f));
+      ScrollPaneFactory.createScrollPane(treePanel, true), findingDetailsPanel, SPLIT_PROPORTION_PROPERTY, 0.5f));
 
     sonarConfigureProject = new SonarConfigureProject();
     notSupportedPanel = centeredLabel("Security Hotspots are currently not supported", "Configure Binding", sonarConfigureProject);
diff --git a/src/main/java/org/sonarlint/intellij/ui/SonarLintLogPanel.java b/src/main/java/org/sonarlint/intellij/ui/SonarLintLogPanel.java
index 356cfffa9b..0289547364 100644
--- a/src/main/java/org/sonarlint/intellij/ui/SonarLintLogPanel.java
+++ b/src/main/java/org/sonarlint/intellij/ui/SonarLintLogPanel.java
@@ -28,7 +28,6 @@
 import com.intellij.openapi.ui.SimpleToolWindowPanel;
 import com.intellij.openapi.wm.ToolWindow;
 import com.intellij.openapi.wm.ex.ToolWindowEx;
-import com.intellij.util.messages.MessageBusConnection;
 import javax.swing.Box;
 import org.sonarlint.intellij.actions.ToolWindowLogAnalysisAction;
 import org.sonarlint.intellij.actions.ToolWindowVerboseModeAction;
@@ -48,7 +47,7 @@ public class SonarLintLogPanel extends SimpleToolWindowPanel {
   private ActionToolbar mainToolbar;
 
   public SonarLintLogPanel(ToolWindow toolWindow, Project project) {
-    super(false, true);
+    super(false, false);
     this.toolWindow = toolWindow;
     this.project = project;
 
@@ -57,7 +56,7 @@ public SonarLintLogPanel(ToolWindow toolWindow, Project project) {
     addConsole();
 
     project.getMessageBus().connect().subscribe(StatusListener.SONARLINT_STATUS_TOPIC, newStatus ->
-      runOnUiThread(project, mainToolbar::updateActionsImmediately));
+    runOnUiThread(project, mainToolbar::updateActionsImmediately));
   }
 
   private void addToolbar() {
diff --git a/src/main/java/org/sonarlint/intellij/ui/SonarLintRulePanel.kt b/src/main/java/org/sonarlint/intellij/ui/SonarLintRulePanel.kt
index 6cb10ceeb3..c9056f46c2 100644
--- a/src/main/java/org/sonarlint/intellij/ui/SonarLintRulePanel.kt
+++ b/src/main/java/org/sonarlint/intellij/ui/SonarLintRulePanel.kt
@@ -63,6 +63,7 @@ import org.sonarlint.intellij.ui.UiUtils.Companion.runOnUiThread
 import org.sonarlint.intellij.ui.ruledescription.RuleDescriptionPanel
 import org.sonarlint.intellij.ui.ruledescription.RuleHeaderPanel
 import org.sonarlint.intellij.ui.ruledescription.RuleLanguages
+import org.sonarlint.intellij.util.runOnPooledThread
 import org.sonarsource.sonarlint.core.rpc.protocol.backend.rules.EffectiveRuleDetailsDto
 
 
@@ -115,7 +116,9 @@ class SonarLintRulePanel(private val project: Project, parent: Disposable) : JBL
         clear()
 
         ApplicationManager.getApplication().messageBus.connect(parent)
-            .subscribe(LafManagerListener.TOPIC, LafManagerListener { updateUiComponents() })
+            .subscribe(
+                LafManagerListener.TOPIC,
+                LafManagerListener { runOnUiThread(project) { updateUiComponents() } })
     }
 
     private data class RuleDetailsLoaderState(val lastModule: Module?, val lastFindingRuleKey: String?, val lastContextKey: String?)
@@ -145,21 +148,23 @@ class SonarLintRulePanel(private val project: Project, parent: Disposable) : JBL
             startLoading()
             ProgressManager.getInstance().run(object : Task.Backgroundable(project, "Loading rule description\u2026", false) {
                 override fun run(progressIndicator: ProgressIndicator) {
-                    SonarLintUtils.getService(BackendService::class.java)
-                        .getActiveRuleDetails(module, ruleKey, ruleDescriptionContextKey)
-                        .orTimeout(30, TimeUnit.SECONDS)
-                        .handle { response, error ->
-                            stopLoading()
-                            ruleDetails = if (error != null) {
-                                SonarLintConsole.get(project).error("Cannot get rule description", error)
-                                null
-                            } else {
-                                response.details()
+                    runOnPooledThread(project) {
+                        SonarLintUtils.getService(BackendService::class.java)
+                            .getActiveRuleDetails(module, ruleKey, ruleDescriptionContextKey)
+                            .orTimeout(30, TimeUnit.SECONDS)
+                            .handle { response, error ->
+                                stopLoading()
+                                ruleDetails = if (error != null) {
+                                    SonarLintConsole.get(project).error("Cannot get rule description", error)
+                                    null
+                                } else {
+                                    response.details()
+                                }
+                                runOnUiThread(project) {
+                                    updateUiComponents()
+                                }
                             }
-                            runOnUiThread(project) {
-                                updateUiComponents()
-                            }
-                        }
+                    }
                 }
             })
         }
@@ -168,7 +173,7 @@ class SonarLintRulePanel(private val project: Project, parent: Disposable) : JBL
 
     fun clear() {
         clearValues()
-        updateUiComponents()
+        runOnUiThread(project) { updateUiComponents() }
     }
 
     private fun clearValues() {
diff --git a/src/main/java/org/sonarlint/intellij/ui/SonarLintToolWindowFactory.java b/src/main/java/org/sonarlint/intellij/ui/SonarLintToolWindowFactory.java
index c77956d7d8..ec39b31f61 100644
--- a/src/main/java/org/sonarlint/intellij/ui/SonarLintToolWindowFactory.java
+++ b/src/main/java/org/sonarlint/intellij/ui/SonarLintToolWindowFactory.java
@@ -39,6 +39,7 @@
 
 import static org.sonarlint.intellij.actions.SonarLintToolWindow.buildTabName;
 import static org.sonarlint.intellij.common.util.SonarLintUtils.getService;
+import static org.sonarlint.intellij.ui.UiUtils.runOnUiThread;
 
 /**
  * Factory of SonarLint tool window.
@@ -54,17 +55,19 @@ public class SonarLintToolWindowFactory implements ToolWindowFactory {
 
   @Override
   public void createToolWindowContent(Project project, final ToolWindow toolWindow) {
-    var contentManager = toolWindow.getContentManager();
-    addCurrentFileTab(project, contentManager);
-    addReportTab(project, contentManager);
-    var sonarLintToolWindow = getService(project, SonarLintToolWindow.class);
-    addSecurityHotspotsTab(project, contentManager);
-    if (SonarLintUtils.isTaintVulnerabilitiesEnabled()) {
-      addTaintVulnerabilitiesTab(project, contentManager);
-    }
-    addLogTab(project, toolWindow);
-    toolWindow.setType(ToolWindowType.DOCKED, null);
-    contentManager.addContentManagerListener(sonarLintToolWindow);
+    runOnUiThread(project, () -> {
+      var contentManager = toolWindow.getContentManager();
+      addCurrentFileTab(project, contentManager);
+      addReportTab(project, contentManager);
+      var sonarLintToolWindow = getService(project, SonarLintToolWindow.class);
+      addSecurityHotspotsTab(project, contentManager);
+      if (SonarLintUtils.isTaintVulnerabilitiesEnabled()) {
+        addTaintVulnerabilitiesTab(project, contentManager);
+      }
+      addLogTab(project, toolWindow);
+      toolWindow.setType(ToolWindowType.DOCKED, null);
+      contentManager.addContentManagerListener(sonarLintToolWindow);
+    });
   }
 
   public static JBSplitter createSplitter(Project project, JComponent parentComponent, Disposable parentDisposable, JComponent c1, JComponent c2, String proportionProperty,
diff --git a/src/main/java/org/sonarlint/intellij/ui/UiUtils.kt b/src/main/java/org/sonarlint/intellij/ui/UiUtils.kt
index af22e8258e..38752c5498 100644
--- a/src/main/java/org/sonarlint/intellij/ui/UiUtils.kt
+++ b/src/main/java/org/sonarlint/intellij/ui/UiUtils.kt
@@ -26,7 +26,7 @@ import com.intellij.openapi.project.Project
 class UiUtils {
     companion object {
         @JvmStatic
-        fun runOnUiThread(runnable: Runnable, modality: ModalityState) {
+        fun runOnUiThread(modality: ModalityState, runnable: Runnable) {
             ApplicationManager.getApplication().invokeLater(runnable, modality)
         }
 
diff --git a/src/main/java/org/sonarlint/intellij/ui/WhatsInThisViewPanel.kt b/src/main/java/org/sonarlint/intellij/ui/WhatsInThisViewPanel.kt
index d233965d74..e5872e7a62 100644
--- a/src/main/java/org/sonarlint/intellij/ui/WhatsInThisViewPanel.kt
+++ b/src/main/java/org/sonarlint/intellij/ui/WhatsInThisViewPanel.kt
@@ -19,7 +19,6 @@
  */
 package org.sonarlint.intellij.ui
 
-import com.intellij.openapi.application.ApplicationManager
 import com.intellij.openapi.project.Project
 import com.intellij.util.ui.JBUI
 import java.awt.CardLayout
@@ -43,7 +42,7 @@ class WhatsInThisViewPanel(val project: Project, private var helpText: String) {
         panel = JPanel(layout)
         createPanel()
         switchCards()
-        runOnUiThread(project) { subscribeToEventsThatAffectCurrentFile(project) { this.switchCards() } }
+        subscribeToEventsThatAffectCurrentFile(project) { this.switchCards() }
     }
 
     private fun createPanel() {
@@ -71,20 +70,14 @@ class WhatsInThisViewPanel(val project: Project, private var helpText: String) {
     }
 
     private fun switchCards() {
-        ApplicationManager.getApplication().assertIsDispatchThread()
-
         // Checking connected mode state may take time, so lets move from EDT to pooled thread
         runOnPooledThread(project) {
-            val projectBindingManager =
-                SonarLintUtils.getService(project, ProjectBindingManager::class.java)
-            projectBindingManager.tryGetServerConnection().ifPresentOrElse(
-                {
-                    switchCard(CONNECTED)
-                }
-            )  // No connection settings for project
+            val projectBindingManager = SonarLintUtils.getService(project, ProjectBindingManager::class.java)
+            projectBindingManager.tryGetServerConnection().ifPresentOrElse({
+                switchCard(CONNECTED)
+            })  // No connection settings for project
             { switchCard(NOT_CONNECTED) }
         }
-
     }
 
     private fun switchCard(cardName: String) {
diff --git a/src/main/java/org/sonarlint/intellij/ui/review/ReviewSecurityHotspotDialog.kt b/src/main/java/org/sonarlint/intellij/ui/review/ReviewSecurityHotspotDialog.kt
index 599dc21a88..52cb991deb 100644
--- a/src/main/java/org/sonarlint/intellij/ui/review/ReviewSecurityHotspotDialog.kt
+++ b/src/main/java/org/sonarlint/intellij/ui/review/ReviewSecurityHotspotDialog.kt
@@ -34,6 +34,7 @@ import org.sonarlint.intellij.core.BackendService
 import org.sonarlint.intellij.documentation.SonarLintDocumentation.Intellij.SECURITY_HOTSPOTS_LINK
 import org.sonarlint.intellij.notifications.SonarLintProjectNotifications
 import org.sonarlint.intellij.ui.UiUtils.Companion.runOnUiThread
+import org.sonarlint.intellij.util.runOnPooledThread
 import org.sonarsource.sonarlint.core.rpc.protocol.backend.hotspot.CheckStatusChangePermittedResponse
 import org.sonarsource.sonarlint.core.rpc.protocol.backend.hotspot.HotspotStatus
 
@@ -62,7 +63,9 @@ class ReviewSecurityHotspotDialog(
 
             override fun doAction(e: ActionEvent) {
                 val status = getStatus()
-                changeStatus(status, ModalityState.stateForComponent(contentPane))
+                runOnPooledThread(project) {
+                    changeStatus(status, ModalityState.stateForComponent(contentPane))
+                }
             }
 
             private fun changeStatus(status: HotspotStatus, modalityState: ModalityState) {
diff --git a/src/main/java/org/sonarlint/intellij/ui/traffic/light/SonarLintDashboardPanel.kt b/src/main/java/org/sonarlint/intellij/ui/traffic/light/SonarLintDashboardPanel.kt
index 158a92d279..ada1959221 100644
--- a/src/main/java/org/sonarlint/intellij/ui/traffic/light/SonarLintDashboardPanel.kt
+++ b/src/main/java/org/sonarlint/intellij/ui/traffic/light/SonarLintDashboardPanel.kt
@@ -53,6 +53,7 @@ import org.sonarlint.intellij.finding.FindingType.ISSUE
 import org.sonarlint.intellij.finding.FindingType.SECURITY_HOTSPOT
 import org.sonarlint.intellij.finding.FindingType.TAINT_VULNERABILITY
 import org.sonarlint.intellij.util.HelpLabelUtils
+import org.sonarlint.intellij.util.runOnPooledThread
 
 
 class SonarLintDashboardPanel(private val editor: Editor) {
@@ -79,9 +80,11 @@ class SonarLintDashboardPanel(private val editor: Editor) {
     private val restartPanel: JPanel
 
     init {
-        editor.project?.let { refreshCheckbox() }
-        focusOnNewCodeCheckbox.addActionListener {
-            getService(CleanAsYouCodeService::class.java).setFocusOnNewCode(focusOnNewCodeCheckbox.isSelected)
+        editor.project?.let { project ->
+            refreshCheckbox()
+            focusOnNewCodeCheckbox.addActionListener {
+                runOnPooledThread(project) { getService(CleanAsYouCodeService::class.java).setFocusOnNewCode(focusOnNewCodeCheckbox.isSelected) }
+            }
         }
         focusOnNewCodeCheckbox.isOpaque = false
 
diff --git a/src/main/java/org/sonarlint/intellij/ui/tree/FlowsTree.java b/src/main/java/org/sonarlint/intellij/ui/tree/FlowsTree.java
index 6c95254831..68e1d8d88d 100644
--- a/src/main/java/org/sonarlint/intellij/ui/tree/FlowsTree.java
+++ b/src/main/java/org/sonarlint/intellij/ui/tree/FlowsTree.java
@@ -40,6 +40,8 @@
 import org.sonarlint.intellij.ui.nodes.FlowSecondaryLocationNode;
 import org.sonarlint.intellij.ui.nodes.PrimaryLocationNode;
 
+import static org.sonarlint.intellij.ui.UiUtils.runOnUiThread;
+
 public class FlowsTree extends Tree {
   private final Project project;
 
@@ -82,9 +84,11 @@ public void treeWillCollapse(TreeExpansionEvent event) throws ExpandVetoExceptio
   }
 
   public void expandAll() {
-    for (var i = 0; i < getRowCount(); i++) {
-      expandRow(i);
-    }
+    runOnUiThread(project, () -> {
+      for (var i = 0; i < getRowCount(); i++) {
+        expandRow(i);
+      }
+    });
   }
 
   private void highlightInEditor(DefaultMutableTreeNode node) {
diff --git a/src/main/java/org/sonarlint/intellij/ui/tree/IssueTreeModelBuilder.java b/src/main/java/org/sonarlint/intellij/ui/tree/IssueTreeModelBuilder.java
index 9f5f05c4e6..8c2b769490 100644
--- a/src/main/java/org/sonarlint/intellij/ui/tree/IssueTreeModelBuilder.java
+++ b/src/main/java/org/sonarlint/intellij/ui/tree/IssueTreeModelBuilder.java
@@ -21,6 +21,7 @@
 
 import com.google.common.collect.ComparisonChain;
 import com.google.common.collect.Ordering;
+import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.vfs.VirtualFile;
 import java.util.Collection;
@@ -46,6 +47,7 @@
 import org.sonarsource.sonarlint.core.client.utils.ImpactSeverity;
 import org.sonarsource.sonarlint.core.rpc.protocol.common.IssueSeverity;
 
+import static org.sonarlint.intellij.ui.UiUtils.runOnUiThread;
 import static org.sonarsource.sonarlint.core.client.utils.ImpactSeverity.HIGH;
 import static org.sonarsource.sonarlint.core.client.utils.ImpactSeverity.LOW;
 import static org.sonarsource.sonarlint.core.client.utils.ImpactSeverity.MEDIUM;
@@ -98,13 +100,13 @@ private SummaryNode getFilesParent() {
   }
 
   public void clear() {
-    updateModel(Collections.emptyMap());
+    runOnUiThread(project, () -> updateModel(Collections.emptyMap()));
   }
 
   public void updateModel(Map<VirtualFile, Collection<LiveIssue>> map) {
     latestIssues = map;
-
     var toRemove = index.getAllFiles().stream().filter(f -> !map.containsKey(f)).toList();
+    ApplicationManager.getApplication().assertIsDispatchThread();
 
     toRemove.forEach(this::removeFile);
 
@@ -129,7 +131,7 @@ public void allowResolvedIssues(boolean allowResolved) {
   }
 
   public void refreshModel(Project project) {
-    updateModel(latestIssues);
+    runOnUiThread(project, () -> updateModel(latestIssues));
     var fileList = new HashSet<>(latestIssues.keySet());
     SonarLintUtils.getService(project, CodeAnalyzerRestarter.class).refreshFiles(fileList);
   }
diff --git a/src/main/java/org/sonarlint/intellij/ui/tree/SecurityHotspotTreeModelBuilder.java b/src/main/java/org/sonarlint/intellij/ui/tree/SecurityHotspotTreeModelBuilder.java
index a216c53707..607478be63 100644
--- a/src/main/java/org/sonarlint/intellij/ui/tree/SecurityHotspotTreeModelBuilder.java
+++ b/src/main/java/org/sonarlint/intellij/ui/tree/SecurityHotspotTreeModelBuilder.java
@@ -21,6 +21,7 @@
 
 import com.google.common.collect.ComparisonChain;
 import com.google.common.collect.Ordering;
+import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.vfs.VirtualFile;
 import java.util.ArrayList;
@@ -100,6 +101,7 @@ private SummaryNode getFilesParent() {
 
   public void updateModel(Map<VirtualFile, Collection<LiveSecurityHotspot>> map) {
     var toRemove = index.getAllFiles().stream().filter(f -> !map.containsKey(f)).toList();
+    ApplicationManager.getApplication().assertIsDispatchThread();
 
     nonFilteredNodes.clear();
     toRemove.forEach(this::removeFile);
diff --git a/src/main/java/org/sonarlint/intellij/ui/vulnerabilities/TaintVulnerabilitiesPanel.kt b/src/main/java/org/sonarlint/intellij/ui/vulnerabilities/TaintVulnerabilitiesPanel.kt
index e5dd616a8e..e267e2d6f8 100644
--- a/src/main/java/org/sonarlint/intellij/ui/vulnerabilities/TaintVulnerabilitiesPanel.kt
+++ b/src/main/java/org/sonarlint/intellij/ui/vulnerabilities/TaintVulnerabilitiesPanel.kt
@@ -133,7 +133,7 @@ class TaintVulnerabilitiesPanel(private val project: Project) : SimpleToolWindow
         treePanel.add(tree)
         treePanel.add(oldTree)
 
-        val treeScrollPane = ScrollPaneFactory.createScrollPane(treePanel)
+        val treeScrollPane = ScrollPaneFactory.createScrollPane(treePanel, true)
         cards.add(createSplitter(project, this, this, treeScrollPane, rulePanel, SPLIT_PROPORTION_PROPERTY, DEFAULT_SPLIT_PROPORTION),
             TREE_CARD_ID
         )
@@ -291,7 +291,7 @@ class TaintVulnerabilitiesPanel(private val project: Project) : SimpleToolWindow
 
     private fun expandDefault() {
         if (taintVulnerabilityTreeUpdater.filteredTaintVulnerabilities.size < 30) {
-            TreeUtil.expand(tree, 2)
+            runOnPooledThread(project) { TreeUtil.expand(tree, 2) }
         } else {
             tree.expandRow(0)
         }
diff --git a/src/main/java/org/sonarlint/intellij/ui/vulnerabilities/tree/TaintVulnerabilityTreeUpdater.kt b/src/main/java/org/sonarlint/intellij/ui/vulnerabilities/tree/TaintVulnerabilityTreeUpdater.kt
index b54ba47a50..e4baf7eab4 100644
--- a/src/main/java/org/sonarlint/intellij/ui/vulnerabilities/tree/TaintVulnerabilityTreeUpdater.kt
+++ b/src/main/java/org/sonarlint/intellij/ui/vulnerabilities/tree/TaintVulnerabilityTreeUpdater.kt
@@ -19,11 +19,13 @@
  */
 package org.sonarlint.intellij.ui.vulnerabilities.tree
 
+import com.intellij.openapi.application.ModalityState
 import org.sonarlint.intellij.finding.FindingType
 import org.sonarlint.intellij.finding.Flow
 import org.sonarlint.intellij.finding.FragmentLocation
 import org.sonarlint.intellij.finding.SameFileFlowFragment
 import org.sonarlint.intellij.finding.issue.vulnerabilities.LocalTaintVulnerability
+import org.sonarlint.intellij.ui.UiUtils.Companion.runOnUiThread
 import org.sonarlint.intellij.ui.nodes.SummaryNode
 import org.sonarlint.intellij.ui.tree.CompactTree
 import org.sonarlint.intellij.ui.tree.CompactTreeModel
@@ -77,7 +79,7 @@ class TaintVulnerabilityTreeUpdater(private val treeSummary: TreeSummary) {
     private fun applyFiltering() {
         val filters = findingFilters()
         filteredTaintVulnerabilities = taintVulnerabilities.filter { vulnerability -> filters.all { filter -> filter.filter(vulnerability) } }
-        model.setCompactTree(createCompactTree(filteredTaintVulnerabilities))
+        runOnUiThread(ModalityState.defaultModalityState()) { model.setCompactTree(createCompactTree(filteredTaintVulnerabilities)) }
         treeSummary.refresh(filteredTaintVulnerabilities.filter { it.file() != null }.groupBy { it.file() }.keys.size, filteredTaintVulnerabilities.size)
     }
 
diff --git a/src/main/java/org/sonarlint/intellij/util/ProjectUtils.java b/src/main/java/org/sonarlint/intellij/util/ProjectUtils.java
index 4256c9c1a0..265a0beaad 100644
--- a/src/main/java/org/sonarlint/intellij/util/ProjectUtils.java
+++ b/src/main/java/org/sonarlint/intellij/util/ProjectUtils.java
@@ -19,6 +19,7 @@
  */
 package org.sonarlint.intellij.util;
 
+import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.project.ProjectCoreUtil;
 import com.intellij.openapi.project.ProjectUtil;
@@ -77,6 +78,7 @@ private static void iterateFilesToAnalyze(Project project, Predicate<VirtualFile
   }
 
   public static PsiFile toPsiFile(Project project, VirtualFile file) throws TextRangeMatcher.NoMatchException {
+    ApplicationManager.getApplication().assertReadAccessAllowed();
     var psiManager = PsiManager.getInstance(project);
     var psiFile = psiManager.findFile(file);
     if (psiFile != null) {
diff --git a/src/main/java/org/sonarlint/intellij/util/SonarLintAppUtils.java b/src/main/java/org/sonarlint/intellij/util/SonarLintAppUtils.java
index 777a954934..d473401172 100644
--- a/src/main/java/org/sonarlint/intellij/util/SonarLintAppUtils.java
+++ b/src/main/java/org/sonarlint/intellij/util/SonarLintAppUtils.java
@@ -65,13 +65,10 @@ public static Project guessProjectForFile(VirtualFile file) {
   }
 
   public static List<VirtualFile> retainOpenFiles(Project project, List<VirtualFile> files) {
-    var openFiles = computeReadActionSafely(project, () -> {
-      if (!project.isOpen()) {
-        return Collections.<VirtualFile>emptyList();
-      }
-      return files.stream().filter(f -> FileEditorManager.getInstance(project).isFileOpen(f)).toList();
-    });
-    return openFiles != null ? openFiles : Collections.emptyList();
+    if (project.isDisposed() || !project.isOpen()) {
+      return Collections.emptyList();
+    }
+    return files.stream().filter(f -> FileEditorManager.getInstance(project).isFileOpen(f)).toList();
   }
 
   /**
diff --git a/src/test/java/org/sonarlint/intellij/actions/SonarAnalyzeAllFilesActionTests.java b/src/test/java/org/sonarlint/intellij/actions/SonarAnalyzeAllFilesActionTests.java
index fb3af5d6e5..77129603ae 100644
--- a/src/test/java/org/sonarlint/intellij/actions/SonarAnalyzeAllFilesActionTests.java
+++ b/src/test/java/org/sonarlint/intellij/actions/SonarAnalyzeAllFilesActionTests.java
@@ -30,6 +30,7 @@
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoInteractions;
 import static org.mockito.Mockito.when;
@@ -77,7 +78,7 @@ void testRun() {
     TestDialogManager.setTestDialog(TestDialog.OK);
     action.actionPerformed(event);
 
-    verify(analysisSubmitter).analyzeAllFiles();
+    verify(analysisSubmitter, timeout(1000)).analyzeAllFiles();
   }
 
 }
diff --git a/src/test/java/org/sonarlint/intellij/actions/SonarAnalyzeChangedFilesActionTests.java b/src/test/java/org/sonarlint/intellij/actions/SonarAnalyzeChangedFilesActionTests.java
index 73de771abd..d0162bdb32 100644
--- a/src/test/java/org/sonarlint/intellij/actions/SonarAnalyzeChangedFilesActionTests.java
+++ b/src/test/java/org/sonarlint/intellij/actions/SonarAnalyzeChangedFilesActionTests.java
@@ -27,11 +27,12 @@
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.sonarlint.intellij.AbstractSonarLintLightTests;
-import org.sonarlint.intellij.analysis.AnalysisSubmitter;
 import org.sonarlint.intellij.analysis.AnalysisStatus;
+import org.sonarlint.intellij.analysis.AnalysisSubmitter;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoInteractions;
 import static org.mockito.Mockito.when;
@@ -87,6 +88,6 @@ void testRun() {
 
     action.actionPerformed(event);
 
-    verify(analysisSubmitter).analyzeVcsChangedFiles();
+    verify(analysisSubmitter, timeout(1000)).analyzeVcsChangedFiles();
   }
 }
diff --git a/src/test/java/org/sonarlint/intellij/trigger/BuildFinishedAnalysisTriggerTests.java b/src/test/java/org/sonarlint/intellij/trigger/BuildFinishedAnalysisTriggerTests.java
index bd8ddd7d6d..2d57434d6f 100644
--- a/src/test/java/org/sonarlint/intellij/trigger/BuildFinishedAnalysisTriggerTests.java
+++ b/src/test/java/org/sonarlint/intellij/trigger/BuildFinishedAnalysisTriggerTests.java
@@ -26,6 +26,7 @@
 import org.sonarlint.intellij.analysis.AnalysisSubmitter;
 
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoInteractions;
 
@@ -43,7 +44,7 @@ void prepare() {
   @Test
   void should_trigger_automake() {
     trigger.buildFinished(getProject(), UUID.randomUUID(), true);
-    verify(submitter).autoAnalyzeOpenFiles(TriggerType.COMPILATION);
+    verify(submitter, timeout(1000)).autoAnalyzeOpenFiles(TriggerType.COMPILATION);
   }
 
   @Test
diff --git a/src/test/java/org/sonarlint/intellij/trigger/CompilationFinishedAnalysisTriggerTests.java b/src/test/java/org/sonarlint/intellij/trigger/CompilationFinishedAnalysisTriggerTests.java
index 5f977efa43..b226c2e17c 100644
--- a/src/test/java/org/sonarlint/intellij/trigger/CompilationFinishedAnalysisTriggerTests.java
+++ b/src/test/java/org/sonarlint/intellij/trigger/CompilationFinishedAnalysisTriggerTests.java
@@ -26,6 +26,7 @@
 import org.sonarlint.intellij.analysis.AnalysisSubmitter;
 
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoInteractions;
 import static org.mockito.Mockito.when;
@@ -46,7 +47,7 @@ void prepare() {
   @Test
   void should_trigger_on_compilation() {
     trigger.compilationFinished(false, 0, 0, context);
-    verify(submitter).autoAnalyzeOpenFiles(TriggerType.COMPILATION);
+    verify(submitter, timeout(1000)).autoAnalyzeOpenFiles(TriggerType.COMPILATION);
   }
 
   @Test
diff --git a/src/test/java/org/sonarlint/intellij/trigger/EditorOpenTriggerTests.java b/src/test/java/org/sonarlint/intellij/trigger/EditorOpenTriggerTests.java
index ed5dcbc85b..b45367c548 100644
--- a/src/test/java/org/sonarlint/intellij/trigger/EditorOpenTriggerTests.java
+++ b/src/test/java/org/sonarlint/intellij/trigger/EditorOpenTriggerTests.java
@@ -29,6 +29,7 @@
 import org.sonarlint.intellij.analysis.AnalysisSubmitter;
 
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoInteractions;
 import static org.mockito.Mockito.when;
@@ -55,7 +56,7 @@ void start() {
   void should_trigger() {
     editorTrigger.fileOpened(editorManager, file);
 
-    verify(analysisSubmitter).autoAnalyzeFile(file, TriggerType.EDITOR_OPEN);
+    verify(analysisSubmitter, timeout(1000)).autoAnalyzeFile(file, TriggerType.EDITOR_OPEN);
   }
 
   @Test
@@ -63,6 +64,6 @@ void should_do_nothing_closed() {
     editorTrigger.fileClosed(editorManager, file);
     editorTrigger.selectionChanged(new FileEditorManagerEvent(editorManager, null, null, null, null));
 
-    verifyNoInteractions  (analysisSubmitter);
+    verifyNoInteractions(analysisSubmitter);
   }
 }
diff --git a/src/test/java/org/sonarlint/intellij/trigger/SonarLintCheckinHandlerTests.java b/src/test/java/org/sonarlint/intellij/trigger/SonarLintCheckinHandlerTests.java
index 9d776397d3..e34e02d72f 100644
--- a/src/test/java/org/sonarlint/intellij/trigger/SonarLintCheckinHandlerTests.java
+++ b/src/test/java/org/sonarlint/intellij/trigger/SonarLintCheckinHandlerTests.java
@@ -47,6 +47,7 @@
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoInteractions;
 import static org.mockito.Mockito.when;
@@ -88,7 +89,7 @@ void testNoUnresolvedIssues() {
     var result = handler.beforeCheckin(null, null);
 
     assertThat(result).isEqualTo(CheckinHandler.ReturnResult.COMMIT);
-    verify(analysisSubmitter).analyzeFilesPreCommit(Collections.singleton(file));
+    verify(analysisSubmitter, timeout(1000)).analyzeFilesPreCommit(Collections.singleton(file));
     verifyNoInteractions(toolWindow);
   }
 
@@ -117,10 +118,10 @@ void testIssues() {
     assertThat(result).isEqualTo(CheckinHandler.ReturnResult.CLOSE_WINDOW);
     assertThat(messages).containsExactly("SonarLint analysis on 1 file found 1 issue");
     ArgumentCaptor<AnalysisResult> analysisResultCaptor = ArgumentCaptor.forClass(AnalysisResult.class);
-    verify(toolWindow).openReportTab(analysisResultCaptor.capture());
+    verify(toolWindow, timeout(1000)).openReportTab(analysisResultCaptor.capture());
     var analysisResult = analysisResultCaptor.getValue();
     assertThat(analysisResult.getFindings().getIssuesPerFile()).containsEntry(file, Set.of(issue));
-    verify(analysisSubmitter).analyzeFilesPreCommit(Collections.singleton(file));
+    verify(analysisSubmitter, timeout(1000)).analyzeFilesPreCommit(Collections.singleton(file));
 
   }
 
@@ -151,9 +152,9 @@ void testSecretsIssues() {
       "\n" +
       "SonarLint analysis found 1 secret. Committed secrets may lead to unauthorized system access.");
     ArgumentCaptor<AnalysisResult> analysisResultCaptor = ArgumentCaptor.forClass(AnalysisResult.class);
-    verify(toolWindow).openReportTab(analysisResultCaptor.capture());
+    verify(toolWindow, timeout(1000)).openReportTab(analysisResultCaptor.capture());
     var analysisResult = analysisResultCaptor.getValue();
     assertThat(analysisResult.getFindings().getIssuesPerFile()).containsEntry(file, Set.of(issue));
-    verify(analysisSubmitter).analyzeFilesPreCommit(Collections.singleton(file));
+    verify(analysisSubmitter, timeout(1000)).analyzeFilesPreCommit(Collections.singleton(file));
   }
 }