9

There is a service class that needs to generate PublicKey instances from X.509-encoded public key representations. One instance of this class will service multiple threads. Is it correct to do something like this?

public class MyService {
    private final KeyFactory rsaKeyFactory;

    public MyService() throws NoSuchAlgorithmException {
        rsaKeyFactory = KeyFactory.getInstance("RSA");
    }

    public PublicKey generatePublicKey(byte[] publicKeyBytes) throws GeneralSecurityException {
        return rsaKeyFactory.generatePublic(new X509EncodedKeySpec(publicKeyBytes));
    }
}

I.e. is the KeyFactory instance used here is thread-safe? generatePublicKey() method may be called by different threads concurrently.

Javadocs don't seem to mention thread-safety.

user207421
  • 305,947
  • 44
  • 307
  • 483
Roman Puchkovskiy
  • 11,415
  • 5
  • 36
  • 72
  • 2
    If the documentation doesn't specify that it's thread-safe, then it's not thread-safe. – Kayaman May 03 '17 at 11:18
  • 2
    As a general rule, if the Javadoc doesn't state otherwise the class is thread-safe. However it's hard to believe you'll be calling this so often that you can't afford to make it `synchronized` just to be sure. – user207421 May 03 '17 at 11:20
  • The more interesting part here: how do you intend to guarantee the "only one instance thing"? – GhostCat May 03 '17 at 11:21
  • @EJP Are you saying thread-safety is the default? I've found it to be the opposite, although that would of course depend on the writer of the docs. – Kayaman May 03 '17 at 11:28
  • @Kayaman *If the documentation doesn't specify that it's thread-safe, then it's not thread-safe.* - [String](https://docs.oracle.com/javase/8/docs/api/java/lang/String.html) makes no mention of thread safety but it IS thread safe. – OldCurmudgeon May 03 '17 at 11:38
  • 2
    @OldCurmudgeon But you only know that because you know String is immutable. Given a class with no mention of thread-safety, you can't tell whether it's thread-safe or not, and I tend to err on the side of caution. This is why I was interested about EJP's "default is thread-safe" comment. – Kayaman May 03 '17 at 11:41
  • @GhostCat This service instance will be created by an IoC controller. I did not mean to say that it will be a strict singleton (one instance per VM). I just meant that one service instance will be used by multiple threads. I will edit the question to make it more clear. – Roman Puchkovskiy May 03 '17 at 11:44
  • 2
    once you create the KeyFactory, it is 100% thread safe to use the generatePublic method (technically no one is there to modify the KeyFactory or its delegate). – hunter May 03 '17 at 11:52
  • @Kayaman I was told it is the default many years ago on the RMI Mailing List by an RMI developer, I forget which one. My question there was in relation to RMI stubs, which are thread-safe, but the general statement was also made. – user207421 May 04 '17 at 02:55

2 Answers2

8

No, if the Javadoc makes no mention of thread-saftey then thread safety is not guaranteed ("synchronization is an implementation detail").[1] Add a synchronized modifier to your generatePublicKey method (or some other form of locking) to make it thread-safe and be sure to add a Javadoc comment noting that it is supposed to be thread-safe.

See also:

  • Is there a standard annotation which should be added to the method's Javadoc to denote that a method should be called on a particular thread?

  • Characterizing thread safety (emphasis mine)

    How many times have you looked at the Javadoc for a class, and wondered, "Is this class thread-safe?" In the absence of clear documentation, readers may make bad assumptions about a class's thread safety. Perhaps they'll just assume it is thread-safe when it's not (that's really bad!), or maybe they'll assume that it can be made thread-safe by synchronizing on the object before calling one of its methods (which may be correct, or may simply be inefficient, or in the worst case, could provide only the illusion of thread safety). In any case, it's better to be clear in the documentation about how a class behaves when an instance is shared across threads.

    [...]

    A class's thread-safety behavior is an intrinsic part of its specification, and should be part of its documentation. Because there is no declarative way of describing a class's thread-safety behavior (yet), it must be described textually. While Bloch's five-tier system for describing a class's degree of thread safety does not cover all possible cases, it's a very good start. Certainly we'd all be better off if every class included this degree of threading behavior in its Javadoc.


But maybe….

It looks like your use might be (that is, as hunter pointed out in the comments, once you have a KeyFactory instance, it might be safe to call KeyFactory#generatePublic from multiple threads).

A bit of source diving, KeyFactory.getInstance(String) looks something like so:[2] [3]

public static KeyFactory getInstance(String algorithm)
        throws NoSuchAlgorithmException {
    return new KeyFactory(algorithm);
}

Which in turns calls:

private KeyFactory(String algorithm) throws NoSuchAlgorithmException {
    this.algorithm = algorithm;
    List<Service> list = GetInstance.getServices("KeyFactory", algorithm);
    serviceIterator = list.iterator();
    // fetch and instantiate initial spi
    if (nextSpi(null) == null) {
        throw new NoSuchAlgorithmException
            (algorithm + " KeyFactory not available");
    }
}

And nextSpi looks like:

private KeyFactorySpi nextSpi(KeyFactorySpi oldSpi) {
    synchronized (lock) {
        // Truncated for brevity
    }
}

And KeyFactory#generatePublic looks something like so:

public final PublicKey generatePublic(KeySpec keySpec)
        throws InvalidKeySpecException {
    if (serviceIterator == null) {
        return spi.engineGeneratePublic(keySpec);
    }
    // Truncated for brevity
}

It does look like the class does some locking in parts and not others, which (I imagine was for a purpose and) means that they took thread-saftey into consideration. It could mean that they had intended for it to be safe to construct and use a factory for the same algorithm on multiple threads but it might also not mean that. You would need to exhaustively check the code paths for race conditions.

That said, please don't build anything assuming a contract other than what's in the Javadoc.

Whymarrh
  • 13,139
  • 14
  • 57
  • 108
0

No, you have to use syncronized blocks if you want thread safe.

developer_hatch
  • 15,898
  • 3
  • 42
  • 75