5

Since StringBuffer is thread safe it can safely be published. Consider the public constructor of StringBuffer ( sources ):

public StringBuffer() {
    super(16);
}

where super(16) designates this one:

AbstractStringBuilder(int capacity) {
    value = new char[capacity];
}

where value declared as

char[] value;

QUESTION: How to publish StringBuffer safely?

I've got the following class:

public class Holder{
     public final StringBuffer sb = new StringBuffer();
}

Can it be considered as safe-publication? I think, it cannot.

final guarantees that we'll see a fresh value of the reference sb. But writing to the sb's internal state within AbstractStringBuilder(int capacity) is not synchronized. Therefore there's no happens-before order which in turn means that read from value occured when invoking sb.append(2); and writing to value in the constructor are racy.

Can you help to understand this? Maybe I missed something...

St.Antario
  • 26,175
  • 41
  • 130
  • 318
  • Do you mean for `sb` to be an instance variable or should that be static? – Paolo Apr 06 '16 at 10:03
  • If you want a thread-safe provider of a text/string that changes at times, then use [`AtomicReference`](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/AtomicReference.html). StringBuffer is for concurrent appending to it, a rare use-case. – Joop Eggen Apr 06 '16 at 10:04
  • @Paolo No it's exactly an instance varialbe. Static initializaition occured when initializaing a class which entails inter-JVM locking which inturns ensure memory consistency. – St.Antario Apr 06 '16 at 10:04
  • I think you are confused about what is or is not safe here. The constructor will have finished before the reference is assigned, there is a happens-before in that regard: if you can see the reference, then you can also see everything that happened in the constructor. The only unsafe thing to do would be to leak `this` from the constructor to something else and that doesn't happen in `StringBuilder`. – Mark Rotteveel Apr 06 '16 at 10:05
  • @JoopEggen The question is not exactly about finding another way. I just want to understand if it's ever considered safe to use StringBuffer. – St.Antario Apr 06 '16 at 10:05
  • @MarkRotteveel _the constructor will have finished before the reference is assigned_ It's not obviosuly to me to say the least. JIT is free to perform any statement reorderings which are consistent with JMM. I got through JMM's happens before defintion and didn't find it.... – St.Antario Apr 06 '16 at 10:06
  • @St.Antario You only assign the reference **after** the constructor has completed, therefor if you can see the assigned reference, then you can also see everything that happened in the constructor (assuming the constructor execution doesn't depend on another thread for initialization). – Mark Rotteveel Apr 06 '16 at 10:08
  • @MarkRotteveel Constructor doesn't have any synchronization semantic. At least it doesn not specified in JLS. Why do you think so? – St.Antario Apr 06 '16 at 10:11
  • *Why* are you wanting to publish `StringBuffer`? This sounds like an XY problem. – chrylis -cautiouslyoptimistic- Apr 06 '16 at 10:12
  • As long as the constructor doesn't leak a reference to itself, then the object is fully constructed before the constructor returns. As you can only assign the reference after the constructor invocation, there is a happens-before(constructor-completion, assignment). And as you can only read the reference after it has been assigned there is also happens-before(assignment, read), and therefor also happens-before(constructor-completion, read). – Mark Rotteveel Apr 06 '16 at 10:17
  • @MarkRotteveel Ah, you relied on the fact that the field was declared `final`, right? – St.Antario Apr 06 '16 at 10:19
  • Looking at the answer by Cootri and re-reading the JLS, I might have been too optimistic ;) – Mark Rotteveel Apr 07 '16 at 07:37

3 Answers3

4

Can you help to understand this? Maybe I missed something...

AFAIR after JSR-133 it is guaranteed that final field of the class that is initialized like in your example is free from race condition during instantiation process and only the correct value will be exposed after init

upd: via Brian Goetz

Under the new memory model, there is something similar to a happens-before relationship between the write of a final field in a constructor and the initial load of a shared reference to that object in another thread. When the constructor completes, all of the writes to final fields (and to variables reachable indirectly through those final fields) become "frozen," and any thread that obtains a reference to that object after the freeze is guaranteed to see the frozen values for all frozen fields. Writes that initialize final fields will not be reordered with operations following the freeze associated with the constructor.

IMHO your question (and concurrency understanding) is very good because it is not an obvious language / platform design feature and it was fixed only in Java SE 5.0

Cootri
  • 3,806
  • 20
  • 30
2

See the javadoc for StringBuffer.

String buffers are safe for use by multiple threads. The methods are synchronized where necessary so that all the operations on any particular instance behave as if they occur in some serial order that is consistent with the order of the method calls made by each of the individual threads involved.

That should be sufficient guarantee.

OldCurmudgeon
  • 64,482
  • 16
  • 119
  • 213
2

Creating the instance variable of sb is thread-safe since it is done at the same time as creating an instance of Holder.

The reason it is thread-safe is it's an instance variable and there is no way thread 1 can start constructing an instance of Holder (and by association a new StringBuffer) and then for a second thread to jump in and start running through the constructor for the same instance.

That is if your code has two threads that both came across the lines

Holder h = new Holder();

You'd end up with two separate instances of Holder and then a race for which is assigned to h, which is a different problem.

Paolo
  • 22,188
  • 6
  • 42
  • 49