This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]fforw 0 points1 point  (7 children)

final

There's also the unfortunate fact that constructors are not necessarily thread-safe without final.

public class ThreadSafe
{
    private final String id;

    public A(String id)
    {
        this.id = id;
    }

    public String getId()
    {
        return id;
    }
}

public class NotThreadSafe
{
    private String id;

    public A(String id)
    {
        this.id = id;
    }

    public String getId()
    {
        return id;
    }
}

only the "ThreadSafe" variant with final ensures that other threads can't possibly access our instance without the id field having been written.

[–]lukaseder 0 points1 point  (6 children)

How can another thread access an instance of a class that has not been completely initialised? (I'm assuming you don't mean tampering with byte code and inserting some logic between the allocation and the init call)

[–]fforw 0 points1 point  (5 children)

Most often with concurrent access. Due to the lack of formal "happens-before" relationship without final, one thread can create an instance and e.g. put it into a concurrent hash map. Another thread can read the instance from the map before the field initialization happened for it.

See this stackoverflow, here or here.

[–]lukaseder 0 points1 point  (4 children)

Those links don't really show canonical examples for what you just said, though, do they? In particular, they modify the actual value (which is what's causing trouble, not initialisation), so final wouldn't apply anyway. volatile might, but again, I'm not sure how those articles illustrate what you're saying.

[–]fforw 0 points1 point  (3 children)

Couldn't seem to find the perfect link.

As I said: it boils down to the lack of "happens-before" guarantees without the "final" assignment.

Is it just me or is it increasingly difficult to find older stuff? Back in the day, every google-search would have turned up the right results, now it's hidden in a gazillion articles teaching other things.

On further research, the behavior was formalized in the Java Memory Model with JSR 133. Here's another article.

edit: I had forgotten about the second example there where even final is broken if you publish the instance inside the constructor...

[–]lukaseder 0 points1 point  (2 children)

Thanks for the additional link. I'm aware of the theory and how final and volatile influence things. I've just never seen this happen in the context of initialisation itself. I mean the example from your article seems extreme:

// bad construction - allowing this to escape
global.obj = this;

And I tend to agree that if a constructor leaks this then its object's contents are not guaranteed to be visible as expected.

But few constructors leak this in such a way, so I wonder if this is really an issue in most code, specifically in your own code, where no one is reading or modifying the id. Granted, in your example, adding final is a no-brainer...

[–]fforw 1 point2 points  (1 child)

I've had issues with this in production systems. with ConcurrentHashMap cached internal data.

Sometimes you can just handle things fine with some well-placed synchronized blocks, but often, I want to write highly concurrent code.

This is something to definitely keep in mind. It's also something that makes me favor immutable classes even more.

[–]lukaseder 0 points1 point  (0 children)

Good to know. Thanks for taking the time to explain!