Java: StringBuilder, static method and possible synchronization problems

Is it possible that such code is not thread safe? This is a static method and we are using a local stringbuilder instance. I suppose the input strings can be held by other objects?

public static String cat(final String ... strings) {

    ...
    ...
    final StringBuilder sb = new StringBuilder(totLength);
    for (int i = 0; i < size; i++) {        
        if (strings[i] != null) {
            sb.append(strings[i]);
        }
    }
    return sb.toString();
}

      

+2


source to share


5 answers


It is not completely thread safe because another thread can modify the same array that was passed in the argument for the parameter strings

. It's not entirely obvious due to the use of varargs, but efficiently (from a thread-safety perspective) the method signature is fair:

public static String cat(String[] strings)

      

It won't throw any exceptions, but you might not see the last values ​​in the array.

As another alternative, you might see something unexpected if it sees a change. For example, suppose we are passing only a single-valued array where the value is first "x":



public static String cat(final String ... strings) {    
    ...
    ...
    final StringBuilder sb = new StringBuilder(totLength);
    for (int i = 0; i < size; i++) {
        // strings[0] is currently "x"
        if (strings[i] != null) {
            // But now another thread might change it to null!
            // At that point we'd get "null" as output
            sb.append(strings[i]);
        }
    }
    return sb.toString();
}

      

In other words, while you probably expect to see β€œx” or β€œin the result, you might seeβ€œ null. ”To fix this, you can only read each value from the array once:

final StringBuilder sb = new StringBuilder(totLength);
for (int i = 0; i < size; i++) {
    String value = strings[i];
    if (value != null) {
        sb.append(value);
    }
}

      

You can still see an array that changes halfway through (for example, if one thread changes from {"x", "y"}

to {"a", "b"}

, you can see "xb" as the result), but you don't get a false zero.

+11


source


This should be Thread Safe, since the strings being passed are immutable. and assuming you are creating totLength

inside a method, everything else is local.

EDIT:



as Jon Skeet points out , it's possible that the vargs value could be passed not only as a sequence of strings (as my answer suggests), but also as a String[]

. In the latter case, there is a possibility that the array will be modified by another thread during processing.

+5


source


No, it is thread safe as written (*). Strings are immutable, so it doesn't matter if this calls multiple threads.

(* for displayed code)

+4


source


It would also be thread safe even if you weren't traversing in immutable objects, because

  • you don't change any of the input parameters
  • the only thing you change is limited to the local scope of the method.
+3


source


See Jon Skeet's answer for the array portion.

As for StringBuilder

, it should be safe. Since it StringBuilder

is created inside a method, each call has its own StringBuilder

.

StringBuilder

itself is not synchronized, so if it existed before the static method was called, you'd be better off using StringBuffer

this instead.

0


source







All Articles