Win a copy of Pro Spring MVC with WebFlux: Web Development in Spring Framework 5 and Spring Boot 2 this week in the Spring forum!
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Ron McLeod
  • Paul Clapham
  • Jeanne Boyarsky
  • Liutauras Vilda
Sheriffs:
  • Rob Spoor
  • Bear Bibeault
  • Tim Cooke
Saloon Keepers:
  • Tim Moores
  • Stephan van Hulst
  • Tim Holloway
  • Carey Brown
  • Piet Souris
Bartenders:
  • Frits Walraven
  • Himai Minh

A quibble about a thread example

 
Saloon Keeper
Posts: 23703
161
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
It's not a matter of what I want. it's a matter of what a compiler can produce for a given set of semantics. Yes, modern compilers can and do re-order code in significant ways. But you cannot reference something before it exists, and as I said, the object "pointer" for a constructed object doesn't exist in the application code space until the constructor method is complete and therefore there's nothing to pass to any other thread until it is. That's a fundamental bottleneck, threading or no.

Unless someone can provide me with an explicit indication in the language spec that construction is not an atomic operation I'm going to be adamant about this. Multi-threading does not mean that you're allowed to ignore timing entirely. There are definite limits in sequential programming languages on how loose you can be with time and still function, multi-threaded or not. You can move stuff around internally all you like, but from the outside, an atomic operation has to act like it was done in the sequence it was coded, regardless. Think of what would happen within a thread if you took the classic 3-statement swap: x=a; a=b; b=x and made it so that the movements ocurred in random order. It would be useless. Similarly. an object is not an object until it's completely an object. The JVM does not simply allocate object memory and then immediately return it before it runs the constructor. It allocates, runs the constructor(s), then and only then dispenses the object pointer to the application code. In fact, I'd be surprised if you can even use the word "synchronized" on a constructor definition for that reason.
 
Saloon Keeper
Posts: 12997
281
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Tim Holloway wrote:the object "pointer" for a constructed object doesn't exist in the application code space until the constructor method is complete and therefore there's nothing to pass to any other thread until it is.


Well, that would imply you couldn't use 'this' inside the constructor.

Unless someone can provide me with an explicit indication in the language spec that construction is not an atomic operation I'm going to be adamant about this.


Suit yourself. The JLS purposely avoids very hard requirements like this. Some JVM implementations may make object creation and initialization atomic. Why would the JLS forbid that? I can only recommend that you go over chapter 17 again and carefully read the sections on happens-before and synchronizes-with. The JLS says that happens-before relationships are established by program order, but this is only true in an intra-thread context and not necessarily in an inter-thread context. Happens-before relationships in an inter-thread context are mainly established through synchronizes-with relationships.

but from the outside, an atomic operation has to act like it was done in the sequence it was coded, regardless.


Once again, the JLS only makes this guarantee in an intra-thread context, or when the application is properly synchronized.

Think of what would happen within a thread if you took the classic 3-statement swap: x=a; a=b; b=x and made it so that the movements ocurred in random order. It would be useless. Similarly. an object is not an object until it's completely an object. The JVM does not simply allocate object memory and then immediately return it before it runs the constructor.


Of course you can't nilly willy reorder every instruction in a program. The JLS says that reordering is legal as long as it doesn't affect the result that the thread that executes the instructions sees. However, if instruction A is allocating memory, instruction B is initializing a field, and instruction C is assigning the memory address of the allocated memory block to a shared variable, the JVM is absolutely free to execute ACB because that has the same result as executing ABC.
 
Tim Holloway
Saloon Keeper
Posts: 23703
161
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
You can, obviously, use "this" inside a constructor. And you can very definitely muck up things big time by mis-using it by assigning it to an external variable outside the constructor. And, again, if you do so, I'll get the baseball bat. Not because it's a language violation. But because the resulting code can be expected to be absolute murder to understand, maintain, and debug. There are better, cleaner ways to work in a multi-threaded environment.

The point was that the example in question did not commit such crimes and therefore carries no such liabilities.

What I am saying is that the pointer to an object is not available to other threads for the taking during the construction process, It can only be given. By a constructor in that process. Short of actually opening up the VM's guts and grabbing around in its internals, which is quite another matter and outside of the specs.

In fact, that's really true of any factory that emits anything that didn't previously exist. If the factory is a "black box", then only the output of the black box is available for inspection and/or modification by other processes. While it's still inside the box, it is not. It's sort of inherent in the whole definition of what a factory is.
 
Master Rancher
Posts: 3889
50
  • Likes 2
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Tim Holloway wrote:Unless someone can provide me with an explicit indication in the language spec that construction is not an atomic operation I'm going to be adamant about this.



Well, no one will find that, because it doesn't exist - the JLS doesn't talk about atomicity as such, except in the context of word tearing of double and long values, and it doesn't define the term.  The JLS also doesn't provide explicit details of all the ways code can fail outside of the guarantees it does provide.  If you can find some sort of guarantee that construction is atomic, great, provide that then.  So far we've been providing plenty of JLS quotes supporting the way Stephan and I believe things work, and you're basically just asserting that it's not true - where do you get your info?  So far the documentation being provided in this thread has been on one side only.  You can continue to be adamant, but it's not going to convince others very well either.

If you have Josh Bloch's Effective Java, Third edition p. 80 (under Item 17: Minimize mutability) does contain the following:

3. Make all fields final. This clearly expresses your intent in a manner that is enforced by the system. Also, it is necessary to ensure correct behavior if a reference to a newly created instance is passed from one thread to another without synchronization, as spelled out in the the memory model [JLS, 17.5; Goetz06, 16].



Yeah, I know, it doesn't say exactly what incorrect behavior could result.  JLS 17.5 already told us of course, you could see f.y == 0.  

Note that the caveat is "if a reference to a newly created instance is passed from one thread to another without synchronization".  That's similar to what you've been saying about only "sane" code... but "synchronization" is a more strict requirement than what yo've been saying, which is basically, don't do something stupid within the constructor.  Bloch is saying that if you actually use other synchronization when passing the reference to the object, you'd get the guarantees you want.  But in the code we've been discussing, the only synchronization is within the addPerson() and containsPerson() methods.  That doesn't cover the passing of the reference to a newly constructed instance of PersonSet.  That could be still be done easily with no synchronization:

And this would not be protected - containsPerson could still throw an NPE because mySet is null.  Probably not, yes.  But it can.
 
Stephan van Hulst
Saloon Keeper
Posts: 12997
281
  • Likes 2
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Mike Simmons wrote:
And this would not be protected - containsPerson could still throw an NPE because mySet is null.  Probably not, yes.  But it can.


In this example it can't, because there's a happens-before relationship between creating the PersonSet and starting the thread, and the start of a thread is a synchronization boundary. If you submit the lambda expression to a thread pool containing an idle thread instead of creating a new thread, your assertion will hold.
 
Ranch Hand
Posts: 35
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I guess ps could be null to the reading thread too
 
Mike Simmons
Master Rancher
Posts: 3889
50
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Stephan- good point, thanks.  (My brain hurts trying to keep track of all these memory model rules.)

Michael- no, local variables like that need to be effectively final in order to be used in a lambda expression.  That lets them ensure that its value is seen by the lambda accurately.  
 
You showed up just in time for the waffles! And this tiny ad:
Building a Better World in your Backyard by Paul Wheaton and Shawn Klassen-Koop
https://coderanch.com/wiki/718759/books/Building-World-Backyard-Paul-Wheaton
reply
    Bookmark Topic Watch Topic
  • New Topic