3

Consider the following Java class:

class X {
    public void foo() {
        bar = 1;
    }

    protected void finalize() {
        if (bar == 1)
            baz();
    }

    private int bar = 0;
}

Under the assumption that X.foo() is never called from any finalize() method (directly or indirectly), can I be sure that the code above is free of data-races, that is, can I be sure that X.finalize() sees the value written by X.foo() in every case where X.foo() is actually called?

The naive analysis would say that X.finalize() cannot run concurrently with X.foo() (due to the mentioned assumption), so no extra synchronization is necessary.

I would guess that the code above is free of data races, but it troubles me that the language specification contains the following explicit statement in §17.4.5, but says nothing about a happens-before relationship between finalize() and methods in general:

There is a happens-before edge from the end of a constructor of an object to the start of a finalizer (§12.6) for that object.

EDIT: I see the need to make my question more precise, so here is an attempt at a precise reformulation of the question:

Does Java guarantee a happens-before relation between a specific method X.foo() and X.finalize() if I guarantee that X.foo() is never called (directly or indirectly) from any finalize() method? Here, happens-before is to be interpreted exactly as defined in §17.4.5.

Kristian Spangsege
  • 2,903
  • 1
  • 20
  • 43
  • What if the finalizer contains other code before the `if`? In general that code might bring the object into scope again which could mean that other threads would have access to it again. But I'm entirely sure about this; I'm highly interested in seeing if anybody can verify or disprove my comment. – Viktor Seifert Dec 12 '13 at 15:36
  • You bring up a very important point that I was not sufficiently aware of. The case where `X.finalize()` contains code that resurrects the instance is not interesting to me (because I control that part of the code, and I know I don't resurrect anything). However, it seems that resurrection can happen in other ways that are outside my control. For example, an application could have a finalizer that resurrects an instance of `X`. – Kristian Spangsege Dec 12 '13 at 17:10

3 Answers3

1

Setting/Getting an int value in Java is atomic anyway AFAIK.

So I don't think you should worry.

"but says nothing about a happens-before relationship
between finalize() and methods in general"

That's because those methods (in general) are not managed/called
by the JVM but by you. So it cannot say anything.

By the way if app code can still call foo(), it means this app
code has reference to your object so your object is not eligible
for garbage collection (i.e. for finalize() to be called).
So, you know, I doubt your concern has grounds at all.

UPDATE:

"Does Java guarantee a happens-before relation between a specific method X.foo() and
X.finalize() if I guarantee that X.foo() is never called (directly or indirectly) from
any finalize() method"

Assume the opposite -> Imagine the situation you get.

The finalize() method was called on object x; then somebody calls foo() on this object x (note: x is already dead i.e. garbage collected) object. Does that sound possible? Not to me.

peter.petrov
  • 38,363
  • 16
  • 94
  • 159
  • My question is general. Think of the `int` as being any type. – Kristian Spangsege Dec 12 '13 at 13:25
  • In general - getting/setting an object reference is also atomic; hm, you might maybe have some problems with long and double (but I doubt it because ... see the updated answer). – peter.petrov Dec 12 '13 at 13:26
  • My question is more general. Please see my attempt at reformulating the question. – Kristian Spangsege Dec 12 '13 at 13:43
  • See my updated answer. Think about it, I think you will get the answer yourself. And if I am totally misunderstanding your concern - let me know please. – peter.petrov Dec 12 '13 at 13:49
  • I bet your conclusion is right, but I don't think you realize the complexity of the question. I need to see if somebody addresses my concerns more thoroughly. – Kristian Spangsege Dec 12 '13 at 13:51
  • I realize the complexity of your question :) I just doubt you will find such a direct guarantee (which you're looking for) in the JLS. But I still don't quite get how you imagine this situation happening. Also, I am sure you know that the JLS hb relation does not define full ordering but only a partial ordering. And in a partial ordering relation (as we know from math), you may well lack that guarantee (which you seek) just because it's partial. So I get your question even from formal standpoint but I don't get it from practical standpoint (i.e. some example of when this could occur). – peter.petrov Dec 12 '13 at 14:02
  • So, if we're not talking hb strictly, I still don't get how your foo() method may be called after or be still running when finalize() is called... Now that I wrote this, I am thinking - why not? :) OK, yeah, I am not sure either. I guess my answer was based more on the practical side of things. – peter.petrov Dec 12 '13 at 14:07
  • As you know, synchronization has two aspects to it, one is atomicity, and the other is enforcement of order. What I am concerned about here is the enforcement of order. I can imagine an implementation of the JVM where a GC thread calls `X.finalize()` in some kind of lock-free way that does not enforce a proper `happens-before` relation between the last user action on the object and the actions of the finalizer. Without some hard guarantees derived directly or indirectly from the JLS, I don't see that I can assume race-free behaviour in my code above. – Kristian Spangsege Dec 12 '13 at 14:12
  • Yeah, I got that. I tend to agree now with you. Yes, if it's not in the JLS, I think you cannot assume that. – peter.petrov Dec 12 '13 at 14:28
1

Since every Thread have an cache of variables it is not thread safe, in other works every thread have its own view of variables. You can apply volatine to the variable to force the Thread load the variable. The same works if the variable is accessed from a synchronized block

-> When does java thread cache refresh happens?

Community
  • 1
  • 1
Chriss
  • 5,157
  • 7
  • 41
  • 75
0

No. You do not have a happen before relationship between foo() and finalize(). This basically means that you might see zero in the method finalize() for the field bar.

I think the java memory model basically says that each thread runs on it own and sees the actions of the other threads in undefined and therefore platform dependent order. Only through the use of the actions described under Happens Before you achieve a defined order.

In your case the finalize() method might not see the value 1 written in the foo method running in a different thread. One reason for this could be, that the two thread run on different cpu cores, which use a memory cache.

Since bugs like this are so hard to find, I developed a tool called vmlens, which detects race conditions using an algorithm called eraser

Viktor Seifert
  • 636
  • 1
  • 7
  • 17
Thomas Krieger
  • 1,607
  • 11
  • 12