【问题标题】:Glassfish 4 - Using Concurrency API to create Managed ThreadsGlassfish 4 - 使用并发 API 创建托管线程
【发布时间】:2014-07-17 00:57:00
【问题描述】:

我正在尝试使用新的并发 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 answer by Per-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();
    }
}

【问题讨论】:

    标签: java multithreading glassfish java-ee-7


    【解决方案1】:

    这与 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();
        }
    }
    

    【讨论】:

      【解决方案2】:

      我怀疑您的 ManagedThreadFactory 没有正确注入,因此“Demo”组件没有启动。

      Java EE 7 规范要求托管线程工厂在 JNDI 中可用,名称为“java:comp/DefaultManagedThreadFactory”,因此请尝试将 @Resource 更改为

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

      我不熟悉 Glassfish(我是 WildFly 类型的人),但您可能在任何 JNDI 树显示中都看不到此参考。它可能在内部链接到“concurrent/__DefaultManagedThreadFactory”(顺便说一句,这不是资源名称)。

      失败了也可以试试

      @Resource(lookup="concurrent/__DefaultManagedThreadFactory")
      

      【讨论】:

      • 感谢您的回复。我更改了注入的@Resource 的名称,但仍然得到相同的行为。但我认为你是对的,似乎 ManagedThreadFactory 没有被正确注入。
      猜你喜欢
      • 1970-01-01
      • 2012-06-24
      • 2020-05-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-06
      • 1970-01-01
      相关资源
      最近更新 更多