Solved

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

Posted on 2010-09-02
14
973 Views
Last Modified: 2012-05-10
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
Comment
Question by:tandtsoftware
  • 7
  • 6
14 Comments
 
LVL 16

Accepted Solution

by:
Valeri earned 425 total points
ID: 33586909
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
 

Author Comment

by:tandtsoftware
ID: 33587280
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
 
LVL 10

Assisted Solution

by:Hegemon
Hegemon earned 75 total points
ID: 33587655
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
3 Use Cases for Connected Systems

Our Dev teams are like yours. They’re continually cranking out code for new features/bugs fixes, testing, deploying, testing some more, responding to production monitoring events and more. It’s complex. So, we thought you’d like to see what’s working for us.

 
LVL 16

Assisted Solution

by:Valeri
Valeri earned 425 total points
ID: 33587854
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
 

Author Comment

by:tandtsoftware
ID: 33588528
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
 

Author Comment

by:tandtsoftware
ID: 33588551
Oops, the line

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

should have read

MessageDigest md = pool.get( key, true );
0
 
LVL 16

Expert Comment

by:Valeri
ID: 33594129
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
 

Author Comment

by:tandtsoftware
ID: 33604436
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
 
LVL 16

Expert Comment

by:Valeri
ID: 33615679
Which implementation of CacheMap you are using?
0
 

Author Comment

by:tandtsoftware
ID: 33619505
I am using my own CacheMap.
0
 
LVL 16

Expert Comment

by:Valeri
ID: 33624679
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
 

Author Comment

by:tandtsoftware
ID: 33740635
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
 
LVL 16

Assisted Solution

by:Valeri
Valeri earned 425 total points
ID: 33746529
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
 

Author Closing Comment

by:tandtsoftware
ID: 33752841
I was able to create a working solution following the advice in the thread combined with my own research and testing.
0

Featured Post

Problems using Powershell and Active Directory?

Managing Active Directory does not always have to be complicated.  If you are spending more time trying instead of doing, then it's time to look at something else. For nearly 20 years, AD admins around the world have used one tool for day-to-day AD management: Hyena. Discover why

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
Checkbox and ListView in Android Layout 4 66
ArrayIndexOutOfBoundException 9 82
factorial example 4 40
hibernate jars 4 31
Introduction This article is the first of three articles that explain why and how the Experts Exchange QA Team does test automation for our web site. This article explains our test automation goals. Then rationale is given for the tools we use to a…
Basic understanding on "OO- Object Orientation" is needed for designing a logical solution to solve a problem. Basic OOAD is a prerequisite for a coder to ensure that they follow the basic design of OO. This would help developers to understand the b…
Viewers will learn one way to get user input in Java. Introduce the Scanner object: Declare the variable that stores the user input: An example prompting the user for input: Methods you need to invoke in order to properly get  user input:
This tutorial covers a practical example of lazy loading technique and early loading technique in a Singleton Design Pattern.

777 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question