Adam Bien's Weblog

Saturday May 28, 2011

@Singleton - The Perfect Cache Facade

@Singleton in the default configuration is the perfect bottleneck. Only one thread a time can access a @Singleton instance.
The annotation @ConcurrencyManagement(ConcurrencyManagementType.BEAN) deactivates this limitation and makes a Singleton instance accessible from multiple threads:

import javax.ejb.Singleton;
import javax.annotation.PostConstruct;
import javax.ejb.ConcurrencyManagement;
import java.util.concurrent.ConcurrentHashMap;
import javax.ejb.ConcurrencyManagementType;

@Singleton
@ConcurrencyManagement(ConcurrencyManagementType.BEAN)
public class Hits {

    private ConcurrentHashMap hits = null;
	
	@PostConstruct
    public void initialize() {
        this.hits = new ConcurrentHashMap();
	}
//cache accessors omitted
}

A java.util.concurrent.ConcurrentHashMap can be accessed by multiple threads consistently without any locking or synchronization required.

[See also page 67, section "Singletons Are Not Completely Dead" in Real World Java EE Night Hacks--Dissecting the Business Tier and the classes Hits and HitsCache in X-ray. X-ray uses a ConcurrentHashMap for deferred writes and is able to handle 1.400 writes/transactions a second - see section "Finding Bugs with Stress Tests" (page 152)]


Special Events: Java 8 with Java EE 7: "More Power with Less Code", 13th October, 2014 and Java EE 7: "Testing and Code Quality", 14th October, 2014

A book about rethinking Java EE Patterns

Comments:

Singleton vs pooled:

By default in Spring, all beans are Singletons and must be written in a stateless way. In EJB, all beans are pooled and can be written _as if_ they were going to be used in a single threaded manner.

If you are writing business logic for EJB, and you don't need share data among threads, wouldn't it be more efficient to write all your beans as concurrent singletons?

Posted by Jon on May 28, 2011 at 09:14 PM CEST #

@Jon,

I don't think it is measurable. Even several hundred parallel requests (threads) will result in a corresponding amount of pooled instances. A @Stateless instance does not contain any shared state and is rather small.

On the other hand a single misplaced "synchronized" on a @Singleton will kill all your concurrency. Putting "synchronized" on @Stateless is strange, but has neither a positive, nor negative impact :-).

With pooling you get also an easy possibility to prevent "Denial of Service Attacks" by setting the maximal number of beans to a reasonable number.

A good point!,

thanks!,

adam

Posted by Adam Bien on May 29, 2011 at 01:15 AM CEST #

456

Posted by 88.85.242.249 on May 29, 2011 at 11:28 AM CEST #

> With pooling you get also an easy
> possibility to prevent "Denial of
> Service Attacks" by setting the
> maximal number of beans to a
> reasonable number.

This sounds interesting. Do I have to set the maximal number in the configuration or in the class itself?
Could You provide a short example of setting the max number?

Posted by Maximilian Eberl on May 29, 2011 at 12:49 PM CEST #

@Maximilian,

these are the pool settings per Bean (=class). In Glassfish you can set it like: <bean-pool>
<steady-pool-size>10</steady-pool-size>
<resize-quantity>10</resize-quantity>
<max-pool-size>100</max-pool-size>
<pool-idle-timeout-in-seconds>600</pool-idle-timeout-in-seconds>
</bean-pool>

(see: http://download.oracle.com/docs/cd/E18930_01/html/821-2417/bearb.html)

All popular application servers do provide this configuration,

thanks!,

adam

Posted by Adam Bien on May 29, 2011 at 04:52 PM CEST #

At the method level you can
specify

@Lock(LockType.READ)

to achieve the same thing.

Posted by Brendan Healey on May 29, 2011 at 05:56 PM CEST #

Hi Adam!
Thx for your book and sharing your pragmatic approaches!

Any idea how to simulate the singleton pattern in JEE5 servers?

Jan

Posted by Jan Wiemer on May 30, 2011 at 09:42 AM CEST #

@Brendan Healey:

@Lock(LockType.READ) also works at the class level.

Posted by dro0x on April 05, 2012 at 04:53 PM CEST #

Hi Adam,

where did you read that it is synchronized by default. Looking at the spec, it uses the container default (http://docs.oracle.com/javaee/6/tutorial/doc/gipvi.html). Did I miss something?

Posted by Steve on August 24, 2012 at 08:45 PM CEST #

Hey Steve,
To answer your question,

>> where did you read that it is synchronized by default

The default ConcurrencyManagementType is 'CONTAINER' as you said and the default LockType is 'WRITE' which means that the singleton session bean should be locked to other clients while a client is calling a method. i.e., only one client can access a method (synchronized) at a time.

Posted by Sriram on July 07, 2014 at 05:39 AM CEST #

Post a Comment:
  • HTML Syntax: NOT allowed
realworldpatterns.com
...the last 150 posts
...the last 10 comments
Links
License