Solved

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

Posted on 2010-09-02
14
999 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 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
Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
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

Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

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

Java had always been an easily readable and understandable language.  Some relatively recent changes in the language seem to be changing this pretty fast, and anyone that had not seen any Java code for the last 5 years will possibly have issues unde…
Go is an acronym of golang, is a programming language developed Google in 2007. Go is a new language that is mostly in the C family, with significant input from Pascal/Modula/Oberon family. Hence Go arisen as low-level language with fast compilation…
Viewers learn how to read error messages and identify possible mistakes that could cause hours of frustration. Coding is as much about debugging your code as it is about writing it. Define Error Message: Line Numbers: Type of Error: Break Down…
Viewers will learn how to properly install Eclipse with the necessary JDK, and will take a look at an introductory Java program. Download Eclipse installation zip file: Extract files from zip file: Download and install JDK 8: Open Eclipse and …

710 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