Go Premium for a chance to win a PS4. Enter to Win

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 1105
  • Last Modified:

One Synchronized MessageDigest or a new MessageDigest per use? Which scales better?

Here are two example Java classes using a MessageDigest to sign some data. Which one would scale better in a very large environment (e.g. where tens of thousands of signings are being performed on many multiple threads)?

SynchronizedMessageDigest creates one MessageDigest and synchronizes access to it, limiting object creation overhead but potentially very briefly blocking threads trying to sign at the same time (note that the signing even more complex examples than this are almost too fast to measure in milliseconds).

PerRequestMessageDigest blocks no threads but creates a new MessageDigest for each request.

public class SynchronizedMessageDigest
{ 
    MessageDigest md;
    
    public SynchronizedMessageDigest() throws Exception
    {
        md = MessageDigest.getInstance( "SHA-512" );      
    }
    
    public synchronized void signMyData(byte[] data, byte[] hash) throws Exception
    {
        try
        {
            md.update( data );
            md.digest( hash );
        }
        catch (Exception e)
        {
            md.reset();
            throw e;
        }
    }
    
    public static void main( String[] args ) throws Exception
    {
        byte[] data = "12345".getBytes();
        byte[] hash = new byte[64];
        new SynchronizedMessageDigest().signMyData(data,hash);
    }
}


public class PerRequestMessageDigest
{ 
    public void signMyData(byte[] data, byte[] hash) throws Exception
    {
        MessageDigest md = MessageDigest.getInstance( "SHA-512" );      
        md.update( data );
        md.digest( hash );
    }
    
    public static void main( String[] args ) throws Exception
    {
        byte[] data = "12345".getBytes();
        byte[] hash = new byte[64];
        new PerRequestMessageDigest().signMyData(data,hash);
    }
}

Open in new window

0
tandtsoftware
Asked:
tandtsoftware
  • 7
  • 6
4 Solutions
 
ValeriCommented:
The best solution is combination from both of them. Create a thread pool with instance of MessageDigest for each one of the threads from the pool.
0
 
tandtsoftwareAuthor Commented:
Ok, good suggestion. Would you try to associate the instances with existing request threads in an application server? (Is there an easy way to do that?) Or would you create an independent pool of threads that do nothing but signing?
0
 
HegemonCommented:
I agree with Valeri's solution, however, instead of creating a home grown thread pool, I'd suggest using the pretty standard way: EJBs, stateless session beans in this case. Of course, if your runtime environment supports them. The container will create a pool of EJBs and you will be able to control the size of the pool, allocations, etc. The EJBs threads will not be tied to  Web container threads and the size of these two thread pools can be different. Finally, this architectures scales up well, i.e. the EJBs can be deployed to more than one server or a cluster.
0
Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

 
ValeriCommented:
Yes, I've been thinking about this :-)
Since different requests are handled by thread pools, it will be possible to create a static Map with MessageDigest for each one of the threads in the pool.
something like that
protected static Map<MessageDigest, Long> mdMap = new HashMap<MessageDigest, Long>();
Long is the identifier of the Thread. And in the request you will be able to get the respective for tihis thread MessageDigest by thread id. If it doesn't exist in the map you will instantiate it and add it to the Map. It will give you better performance than the solution with the independent thread.
But the problem here is that thread pools that handle the requests have unpredictable behaviour.
From time to time threads used for handling the requests die silently and new threads are instantiated, so your Map could get bigger and bigger in the time, so somehow you have to delete the respective items in your Mar that doesn't correspond to the alive threads in the pool, or to remove items not used for more than 1 day let say, and it will solve this problem. In my opinion it's the best solution.
0
 
tandtsoftwareAuthor Commented:
How about this? It's more of a cache solution than a pool, using a CacheMap utility that automatically removes entries after a specified idle time. I'm thinking a even one minute idle time is okay; if the thread isn't active then the CacheMap will remove its reference to the MessageDigest so it will be garbage collected. This solution really isn't a pool because it doesn't allow multiple threads to share idle MessageDigests.
public class MessageDigestCache
{
    private final static long MaxIdleTimeToLive = 60000L; // one minute

    private static CacheMap<String,MessageDigest> pool = new CacheMap<String,MessageDigest>();
    
    public synchronized static MessageDigest getMessageDigest(String algorithm) throws NoSuchAlgorithmException
    {
        String key = algorithm + ":" + Thread.currentThread().getId();
        MessageDigest md = pool.get( Thread.currentThread().getId(), true ); // get MD from pool and reset idle life timer
        if (md == null)
        {
            md = MessageDigest.getInstance(algorithm);
            pool.put( key, md, MaxIdleTimeToLive );
        }
        return md;
    }
}

Open in new window

0
 
tandtsoftwareAuthor Commented:
Oops, the line

MessageDigest md = pool.get( Thread.currentThread().getId(), true );

should have read

MessageDigest md = pool.get( key, true );
0
 
ValeriCommented:
Hm, looks great! :-)
>> I'm thinking a even one minute idle time is okay
I don't know?! If you have a lot of requests it's enough.
0
 
tandtsoftwareAuthor Commented:
I am now, after further research and experimentation, going to agree that the maximum idle time should be more like one day so long as the timer thread implemented by the CacheMap is a daemon thread (which will not block the application from terminating).
0
 
ValeriCommented:
Which implementation of CacheMap you are using?
0
 
tandtsoftwareAuthor Commented:
I am using my own CacheMap.
0
 
ValeriCommented:
You don't have to worry about the timer thread. You can close it by invoking cancel() method at the end of your application, so your app will terminate in normal way.
0
 
tandtsoftwareAuthor Commented:
It depends on how tightly I want CacheMap to be tied to my application vs. have it behave as framework. I would rather the "consumer" of CacheMap to not have to do anything special. So I am thinking my choices are (1) to make it operate as a daemon thread or (2) require the "consumer" to call something that in turn "cancels()" the thread it is using to expire old entries.
0
 
ValeriCommented:
In my opinion (1) is the better choice. If this thread starts on every 2 hours, it should remove the entries not used for more than 2 hours.
0
 
tandtsoftwareAuthor Commented:
I was able to create a working solution following the advice in the thread combined with my own research and testing.
0

Featured Post

What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

  • 7
  • 6
Tackle projects and never again get stuck behind a technical roadblock.
Join Now