Still celebrating National IT Professionals Day with 3 months of free Premium Membership. Use Code ITDAY17

x
?
Solved

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

Posted on 2010-09-02
14
Medium Priority
?
1,071 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 1700 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 300 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
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 
LVL 16

Assisted Solution

by:Valeri
Valeri earned 1700 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 1700 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

Build and deliver software with DevOps

A digital transformation requires faster time to market, shorter software development lifecycles, and the ability to adapt rapidly to changing customer demands. DevOps provides the solution.

Question has a verified solution.

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

Java Flight Recorder and Java Mission Control together create a complete tool chain to continuously collect low level and detailed runtime information enabling after-the-fact incident analysis. Java Flight Recorder is a profiling and event collectio…
In this post we will learn how to make Android Gesture Tutorial and give different functionality whenever a user Touch or Scroll android screen.
Viewers will learn about if statements in Java and their use The if statement: The condition required to create an if statement: Variations of if statements: An example using if statements:
Viewers will learn about the regular for loop in Java and how to use it. Definition: Break the for loop down into 3 parts: Syntax when using for loops: Example using a for loop:
Suggested Courses

722 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