1

Here is my problem. I want to document a piece of program and want to express something, I believed quite common among the programming community, but can't hack the idea into a word. Take a look at this class :

public class RemoteClient {
    private InetSocketAddress remoteAddress;
    /*+ some other fields not in the scope of this topic*/

    public RemoteClient(String hostName, int port
    /*+ some other arguments not in the scope of this topic*/){
       this.remoteAddress=new InetSocketAddress(hostName, port);
    }

    public InetSocketAddress getRemoteAddress() {
        return this.remoteAddress;
    } 

}

The obvious problem in a multithreaded environment is that the caller of the constructor has no guaranty that hostName will be resolved, and therefore build time is not deterministic. It would be, I believe, much better if the construction of InetSocketAddress was lazy, such as follow :

public class RemoteClient {
    private volatile InetSocketAddress remoteAddress; 
    private final String hostName;
    private final int port; 
    /*+ some other fields not in the scope of this topic*/

    public RemoteClient(String hostName, int port
    /*+ some other arguments not in the scope of this topic*/){
       this.hostName=hostName;
       this.port=port;
    }

    /** lazy instanciation **/
    public InetSocketAddress getRemoteAddress() {
        if(remoteAddress==null){
            synchronized(hostName){
                if(remoteAddress==null){
                    this.remoteAddress=new InetSocketAddress(hostName, port);
                }
            } 
        }
        return this.remoteAddress;
    } 

}

So in this case, the Thread calling the constructor operator, which might at some time build from a list of host names, has no more network-related indetermination. It is now delegated to the Thread in charge of communication with the remote client.

    My questions are :
  • can you give reasons why the second proposition is/isn't better designed?
  • could you put a word on the idiom behind this practice ?

I believe this is not a opinion-based topic, as I'm asking for a design.

Jules Sam. Randolph
  • 3,610
  • 2
  • 31
  • 50
  • `double-checked locking`? – EpicPandaForce Oct 23 '14 at 23:14
  • Lazy initialization seems a closer fit. Double check locking is something you have to do to achieve it. – pimaster Oct 23 '14 at 23:20
  • beside the fact that you actually do use the double checked lock pattern I would say the pattern you are looking for is already in your comment: **lazy instanciation**. The *blessed* term is **Lazy Loading**. – Oncaphillis Oct 23 '14 at 23:21
  • @Zhuinden not really... what I mean is "give any instantiation a constant build time", "avoid using non-deterministic, ie network-related code during instantiation", "delegate network-related code access to specific threads"... some of you might answer "this is common sense", but there must be a word. – Jules Sam. Randolph Oct 23 '14 at 23:22
  • @Oncaphillis and pimaster : Yes sure... but isn't there a more abstract notion where **lazy loading** becomes *the way to achieve it*? – Jules Sam. Randolph Oct 23 '14 at 23:26
  • @user2779871 Hmm **Virtual Proxy** comes to mind. – Oncaphillis Oct 23 '14 at 23:31
  • Thanks a lot, I'll read about it. – Jules Sam. Randolph Oct 24 '14 at 00:12
  • Honestly? Code Smell sums it up for me. You don't add any value for multi-threading (the double checked locking idiom is a coding error). You don't need to synchronize with `volatile` (at least not in this case). Just assign it if it's null, it's fine. – markspace Oct 24 '14 at 00:14

3 Answers3

2

The obvious problem in a multithreaded environment is that the caller of the constructor has no guaranty that hostName will be resolved, and therefore build time is not deterministic.

I don't see that. I don't see why this is an 'obvious problem in a multi-threaded environment', and I don't see why 'non-deterministic construction time' is a problem in any particular environment at all. If construction time is a problem, it is a problem in any environment. If what you're looking for is a shorter DNS timeout, there are ways to enforce that: see the Networking Properties page in the Javadoc.

It would be, I believe, much better if the construction of InetSocketAddress was lazy.

In that case all you need is InetSocketAddress.createUnresolved().

user207421
  • 305,947
  • 44
  • 307
  • 483
  • Use case : a thread A gets information about hosts, from a peculiar source. Each time it receives information about a new host, he will instantiate it and map it. He does not want the hostName to be resolved, because at this stage, the host is not necessary useful. It will be resolved by a Thread B which will be in charge of communication with this specific host. By the way, I'm not using `InetSocketAddress` to construct a `Socket`; but a `DatagramPacket`. An other advantage of any `InetAddress` is that if domain is unresolved **you'll keep a trace of that**, avoiding useless resources usage. – Jules Sam. Randolph Oct 24 '14 at 01:37
  • NB : `new InetSocketAddress(hostName, port);` does compile home, jdk8u25. – Jules Sam. Randolph Oct 24 '14 at 01:38
  • In that case all you need is [`InetSocketAddress.createUnresolved()`](http://docs.oracle.com/javase/7/docs/api/java/net/InetSocketAddress.html#createUnresolved(java.lang.String,%20int)). – user207421 Oct 24 '14 at 01:45
2

The simple, "do it at constructor time method" that you dislike has a couple of big advantages:

  1. remoteAddress can be final and more thread-safe, e.g. leaky constructors. And it avoids all the double locking crap for simplicity. (BTW, unless you are calling getRemoteAddress() a gazillion times, just synchronize your lazy call.)
  2. If caller gives an invalid port, it will fail fast (throw an IllegalArgumentException) in the constructor which is likely a better place to handle the error. The lazy version of getRemoteAddress() will fail much later with an IllegalArgumentExceptionin a method that has no arguments, which is very confusing. Plus by then it is probably too late to take any corrective action (like popping back up to the UI level to ask again in a dialog).

For the record, I agree with EJP that a non-deterministic speed constructor is unlikely to be a problem anyway.

user949300
  • 15,364
  • 7
  • 35
  • 66
  • 100% agree. I think the best solution here is, as @EJB suggested, `InetSocketAddress.createUnresolved(String host, int port)` at construction time ; provides me the advantages of both solutions. – Jules Sam. Randolph Oct 24 '14 at 02:03
  • 1
    Kudos to EJP for `createUnresolved()` - I never knew about it until today. – user949300 Oct 24 '14 at 02:06
0

You don't need to use synchronized in this case with a volatile. The volatile does all the work for you. C.f. the Java Language Spec:

A write to a volatile variable v (§8.3.1.4) synchronizes-with all subsequent reads of v by any thread (where "subsequent" is defined according to the synchronization order).

And this StackOverflow answer on Safe Publication.

public class RemoteClient {
    private volatile InetSocketAddress remoteAddress; 
    private final String hostName;
    private final int port; 
    /*+ some other fields not in the scope of this topic*/

    public RemoteClient(String hostName, int port
    /*+ some other arguments not in the scope of this topic*/){
       this.hostName=hostName;
       this.port=port;
    }

    /** lazy instanciation **/
    public InetSocketAddress getRemoteAddress() {
       if(remoteAddress==null){
          this.remoteAddress=new InetSocketAddress(hostName, port);
       }
       return this.remoteAddress;
    } 

}
Community
  • 1
  • 1
markspace
  • 10,621
  • 3
  • 25
  • 39
  • That sounds wrong to me. `A write to a volatile variable synchronizes-with all subsequent reads` which means synchronized *while writing*. In your snippet, two threads could consequently reach the line `if(remoteAddress==null)` and therefore create each of them one instance, even though one of them will do it first because of this quote you gave about `volatile`. Double check is not an option. `volatile` **allows synchronisation on writings, not readings.** – Jules Sam. Randolph Oct 24 '14 at 00:36
  • Well, all sorts of weird stuff here. Yes, two objects could be created. However, everything else you said is wrong. The spec means what it says, even if it "seems wrong." Synchronization in Java is almost completely concerned with data-races: a write followed by a subsequent read. Too much to go into here. If you're really dubious, ask another question about `volatile` and synchronization with subsequent reads so you can get a more complete answer. – markspace Oct 24 '14 at 01:02
  • Sorry my last sentence was not very clear. English is not my native language. I meant that synchronization starts when a reference is written onto the field. So to say, I do need a synchronization block that would nest my read operation `==null` and write operation. Otherwise, even with the volatile key, I might have N instances of the object hanging around. Having N instances isn't such a problem? – Jules Sam. Randolph Oct 24 '14 at 01:24
  • Synchronization actually happens after the write,not during. You are probably thinking of MUTEX, which `volatile` does not provide at all. As for N instances of `InetSocketAddress`, they *are* immutable, so maybe not as much problem as you think. – markspace Oct 24 '14 at 01:33
  • Do you mean that if thread A is doing `new Object()` over a `volatile` reference, thread B reading this reference will see `null`? Seems weird to me! I thought `volatile` prevented those data-race cases, and would force thread B to wait until a non null reference is provided through writing operation. I really wish to understand it better... – Jules Sam. Randolph Oct 24 '14 at 02:10
  • There's no "over". References are atomic; thread B either sees the reference or it sees null. No other value is possible. What volatile does is make all fields inside of the object visible. It's buried in the spec but synchronization applies to all writes by a thread up to the point of synchronization. `volatile` also guarantees the reference is visible "immediately", which can be important too. – markspace Oct 24 '14 at 02:52
  • Sorry to annoy you with my questions. But if references are atomic as you suggest, why is there such thing as `java.util.concurrent.atomic.AtomicReference`? I read for `C` and `C++` that `volatile` forbid compiler to use cpu registries, which mean only direct RAM accesses are allowed / no caching. Is it the same for java? – Jules Sam. Randolph Oct 24 '14 at 03:27
  • [When to use AtomicReference.](http://stackoverflow.com/questions/3964211/when-to-use-atomicreference-in-java) Primarily it's the `compareAndSwap()` method, which you can't do atomically with just the bare reference. Yes, the Java volatile is different than C and C++, Java's volatile works more like the `synchronized` keyword -- it fully synchronizes all writes, not just the write to the volatile field itself. Also read: [Java Concurrency in Practice](http://jcip.net.s3-website-us-east-1.amazonaws.com/) – markspace Oct 24 '14 at 03:38