Solved

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

Posted on 2010-09-02
14
941 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
 
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
IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

 

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

6 Surprising Benefits of Threat Intelligence

All sorts of threat intelligence is available on the web. Intelligence you can learn from, and use to anticipate and prepare for future attacks.

Join & Write a Comment

Suggested Solutions

For customizing the look of your lightweight component and making it look lucid like it was made of glass. Or: how to make your component more Apple-ish ;) This tip assumes your component to be of rectangular shape and completely opaque. (COD…
Java contains several comparison operators (e.g., <, <=, >, >=, ==, !=) that allow you to compare primitive values. However, these operators cannot be used to compare the contents of objects. Interface Comparable is used to allow objects of a cl…
Viewers will learn about arithmetic and Boolean expressions in Java and the logical operators used to create Boolean expressions. We will cover the symbols used for arithmetic expressions and define each logical operator and how to use them in Boole…
Viewers will learn about basic arrays, how to declare them, and how to use them. Introduction and definition: Declare an array and cover the syntax of declaring them: Initialize every index in the created array: Example/Features of a basic arr…

760 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

Need Help in Real-Time?

Connect with top rated Experts

18 Experts available now in Live!

Get 1:1 Help Now