@NonNull
    public static synchronized ScheduledExecutorService get() {
        if (executorService == null) {
            // corePoolSize is set to 10, but will only be created if needed.
            // ScheduledThreadPoolExecutor "acts as a fixed-sized pool using corePoolSize threads"
            // TODO consider also wrapping in ContextResettingExecutorService
            executorService = new ImpersonatingScheduledExecutorService(
                    new ErrorLoggingScheduledThreadPoolExecutor(10, new NamingThreadFactory(new ClassLoaderSanityThreadFactory(new DaemonThreadFactory()), "jenkins.util.Timer")),
                    ACL.SYSTEM2);
        }
        return executorService;
    }
@Test
    @Issue("JENKINS-49206")
    public void timerBogusClassloader() throws Exception {
        final int threadCount = 10;  // Twice Timer pool size to ensure we end up creating at least one new thread
        final CountDownLatch startLatch = new CountDownLatch(threadCount);

        final ClassLoader[] contextClassloaders = new ClassLoader[threadCount];
        ScheduledFuture<?>[] futures = new ScheduledFuture[threadCount];
        final ClassLoader bogusClassloader = new GroovyClassLoader();

        Runnable timerTest = new Runnable() {
            @Override
            public void run() {
                ClassLoader cl = Thread.currentThread().getContextClassLoader();
                Thread.currentThread().setContextClassLoader(bogusClassloader);
                ScheduledExecutorService exec = Timer.get();
                for (int i = 0; i < threadCount; i++) {
                    final int j = i;
                    futures[j] = exec.schedule(new Runnable() {
                        @Override
                        public void run() {
                            startLatch.countDown();
                            contextClassloaders[j] = Thread.currentThread().getContextClassLoader();
                        }
                    }, 0, TimeUnit.SECONDS);
                }
                Thread.currentThread().setContextClassLoader(cl);
            }
        };

        Thread t = new Thread(timerTest);
        t.start();
        t.join(1000L);

        for (int i = 0; i < threadCount; i++) {
            futures[i].get();
            assertEquals(Timer.class.getClassLoader(), contextClassloaders[i]);
        }
    }