Glassfish 4 - 使用并发API创建托管线程

时间:2014-05-27 23:56:51

标签: java multithreading glassfish java-ee-7

我正在尝试使用新的并发API来注入ManagedThreadFactory并按the Oracle tutorial使用它。

这是我正在谈论的一个例子:

@Singleton
@Startup
public class Demo {
    @Resource(name="concurrent/__DefaultManagedThreadFactory") ManagedThreadFactory threadFactory;

    @PostConstruct
    public void startup() {
        threadFactory.newThread(
            new Runnable() {
                @Override
                public void run() {
                    System.out.println("Do something.");
                }
            }
        ).start();
    }
}

我正在使用Glassfish插件在Eclipse中开发。当我在进行更改后重新发布时,我总是在服务器日志中获得此行。每次调用start()时都会出现一次:

SEVERE: java.lang.IllegalStateException: Module (my application) is disabled

它实际上并没有抛出IllegalStateException,只是报告一个已经被抛出(并被捕获)到Glassfish中。应用程序正常部署,但没有任何线程启动。如果我随后重新发布并第二次,“错误”消失,线程按预期启动。

当我尝试将应用程序部署到“真正的”Glassfish设置(没有Eclipse)时,它总是报告成功部署,并且日志不包含“错误”。但它仍然无法启动线程(即使重复部署)。

我是否正确使用了并发API?可能是配置问题?对于记录,如果我使用ManagedExcecutorService,我会得到相同的行为。


为了记录,几个月前在这里问了这个问题:Can I start a ManagedThread in a Singleton Enterprise Java Bean?,但它没有得到真正的回答,除了再问一遍,我还没有做任何事情的声誉。对不起!


更新:This answerPer-Axel Felth有效。谢谢!我对该解决方案进行了一些重构,试图将变通代码与原始应用程序逻辑隔离开来:

@Singleton
@Startup
public class Demo {

    @Resource(name="java:comp/DefaultManagedThreadFactory") ManagedThreadFactory threadFactory;
    @EJB private ConcurrencyInitializer concurrencyInitializer;
    @EJB private Demo self;

    @PostConstruct
    public void startup() {
        self.startThread();
    }

    @Asynchronous
    public void startThread() {
        //This line applies the workaround
        concurrencyInitializer.init();

        //Everything beyond this point is my original application logic
        threadFactory.newThread(
            new Runnable() {
                @Override
                public void run() {
                    System.out.println("Do something.");
                }
            }
        ).start();            
    }

}

/**
 * A utility class used to get around a bug in Glassfish that allows
 * Concurrency resources (ManagedThreadFactory, ManagedExecutorService, etc)
 * to be injected before they are ready to be used. 
 * 
 * Derived from solution by Per-Axel Felth in: https://stackoverflow.com/questions/23900826/glassfish-4-using-concurrency-api-to-create-managed-threads
 */
@Singleton
public class ConcurrencyInitializer {
    /**
     * The number of milliseconds to wait before try to 
     */
    public static final long RETRY_DELAY = 500L;

    /**
     * The maximum number of concurrency attempts to make before failing
     */
    public static final int MAX_RETRIES = 20;

    /**
     * Repeatedly attempts to submit a Runnable task to an injected ManagedExecutorService
     * to trigger the readying of the Concurrency resources.
     * 
     * @return true if successful (Concurrency resources are now ready for use),
     *         false if timed out instead
     */
    public boolean init() {
        final AtomicBoolean done = new AtomicBoolean(false);
        int i = 0;

        try {
            while (!done.get() && i++ < MAX_RETRIES) {
                executorService.submit(new Runnable() {
                    @Override
                    public void run() {
                        done.set(true);
                    }
                });
                Thread.sleep(RETRY_DELAY);
            }
        } catch(InterruptedException e) {
            //Do nothing.
        } 

        return done.get();
    }
}

2 个答案:

答案 0 :(得分:2)

这与Glassfish错误有关。我不久前遇到了同样的错误并构建了一个解决方法。事实是,线程工厂注入正常,但如果你“太早”使用它,你最终会得到一个IllegalStateException。

我的解决方法代码如下所示。它使用注入的执行程序服务来检测何时加载应用程序并且并发工具可用,然后在方法init中执行实际的启动逻辑。

@Singleton
@Startup
public class Demo {

    @Resource(name = "concurrent/__DefaultManagedThreadFactory")
    ManagedThreadFactory threadFactory;
    @Resource
    ManagedExecutorService executorService;
    @EJB
    Demo me;

    @PostConstruct
    public void startup() {

        me.waitAndInitialize();
    }

    @Asynchronous
    public Future<?> waitAndInitialize() {
        try {
            final AtomicInteger done = new AtomicInteger(0);
            int i = 0;

            while (done.intValue() == 0 && i < 20) {
                System.out.println("Is executor service up?");

                i++;

                executorService.submit(
                        new Runnable() {

                            @Override
                            public void run() {
                                int incrementAndGet = done.incrementAndGet();
                                System.out.println("Run by executorservice");
                            }
                        });
                Thread.sleep(500);
            }

            if (done.intValue() == 0) {
                Logger.getAnonymousLogger().severe("Waited a long time for the ExecutorService do become ready, but it never did. Will not initialize!");
            } else {
                init();
            }
        } catch (Exception e) {
            Logger.getAnonymousLogger().log(Level.SEVERE, "Exception in waitAndInitialize: " + e.getMessage(), e);
        }

        return new AsyncResult<>(null);
    }

    private void init() {
        threadFactory.newThread(
                new Runnable() {
                    @Override
                    public void run() {
                        System.out.println("Do something.");
                    }
                }
        ).start();
    }
}

答案 1 :(得分:0)

我怀疑你的ManagedThreadFactory没有正确注入,所以&#34; Demo&#34;组件未启动。

Java EE 7规范要求在JNDI中使用名为&#34; java:comp / DefaultManagedThreadFactory&#34;的托管线程工厂,因此尝试将@Resource更改为

@Resource(name="java:comp/DefaultManagedThreadFactory")

我不熟悉Glassfish(我是一个WildFly类型的人)但你可能在任何JNDI树形显示中都看不到这个引用。它可以在内部链接到&#34; concurrent / __ DefaultManagedThreadFactory&#34; (这不是资源名称btw)。

如果失败,你也可以尝试

@Resource(lookup="concurrent/__DefaultManagedThreadFactory")