CMZS
CMZS

Reputation: 601

Java multi-threading: thread safe data structure vs. synchronized method

I have a class TestLogger that has a void method log(String s), which may be accessed by multiple threads. Here is my code

public class TestLogger {
    private static final StringBuffer buffer = new StringBuffer();

    public static void log(String s) {
        buffer.append(s);
    }
}

I am not sure here if I used the thread safe class StringBuffer, do I still need to put synchronized keyword on the method log(String) to ensure thread safety of the method? And how about this method

public static void log(String s, int type) {
    if (type == 0)
        buffer.append(s);
    if (type == 1)
        buffer.append("SOME HEADER " + s);
}

here type is not modified in the method log. Do I need to use synchronized keyword?

In Java, there are both synchronized keyword and thread safe classes that can provide thread safety. I am not sure when to use one and the other?

Upvotes: 1

Views: 1991

Answers (4)

Solomon Slow
Solomon Slow

Reputation: 27115

And how about this method...

Your two methods are equally thread-safe (or not!, see below the line). No matter which method is called, the same thing will happen: Exactly one string will be added to the shared buffer.


Using thread-safe objects does not make a program thread-safe. It's up to you to decide what "thread-safe" means.

When somebody tells you that some class is thread-safe, what they're promising is, that there's no way that multiple threads calling the class's methods can make any of them behave in "wrong" ways.

And what does "wrong" mean? Well, it depends. Certainly, any behavior that disagrees with the class's documentation would be wrong. And often, any behavior that disagrees with what a reasonable programmer would expect could be called wrong too.

In the case of StringBuffer, here's what "thread-safe" means: It means, that:

  • You won't find anything in the buffer that your program didn't put in the buffer.
  • You won't find anything missing from the buffer that your program did put in there.
  • You won't find the characters from two different strings interleaved with one another, and finally,
  • If you can prove that string A was appended before string B was appended, then string A will appear in the output before string B.

Your example method calls are thread safe simply by virtue of the fact that each one makes just one call on a thread-safe shared object.

If your example had several shared objects though, that's when the individual thread-safety of the objects might not add up to thread-safety for the whole algorithm.

Upvotes: 3

Channa Jayamuni
Channa Jayamuni

Reputation: 1905

StringBuilder is not thread safe in java. So you can use StringBuffer which is thread safe.

The synchronized keyword can be used in two different ways.

Synchronized Methods: It makes the method thread-safe.

public synchronized static void log (String log) { 
        buffer.append(log);
}

Synchronized Statements: It make the specifed object thread-safe.

public static void log(String log) {
    synchronized (buffer) {
        buffer.append(log);
    }
}

Upvotes: -1

Mureinik
Mureinik

Reputation: 311393

Since either implementation of this method accesses buffer at most once, you don't technically need to synchronize the method, but it's a really bad practice.

First, it's extremely fragile. It's enough that some careless developer takes a look at this method and decides to "optimize" it (to better Java code!) for you to lose the StringBuffer's synchronization:

public static void log(String s, int type) {
    if (type == 0)
        buffer.append(s);
    if (type == 1)
        buffer.append("SOME HEADER ").optimize(s);
}

This snippet shows two separate calls to append, so if you concurrently call log('A', 1) and log('B',1), your resulting buffer may very well be "SOME HEADER SOME HEADER AB".

Second, even if you don't technically break synchronization, by relying solely on the StringBuffer to supply your synchronization needs, you may discover slight behavior oddities. For example, consider a future request to also log the message's time:

public static void log(String s) {
    Date d = new Date();
    buffer.append(d.toString() + " " + s);
}

If you have a considerable number of concurrent calls to this method, you may experience thread A creating the new Date instance, context switching to another thread that completes the entire method and then returning to thread A. This will make your log appear as though it's moving back in time, which is definitely not what you want.

And third, and most importantly, defining the method as synchronized has a declarative value. It conveys the information of how this method behaves in a multithreaded context and how you'd expect two concurrent call to react to each other. In a public utility function designed to be used by others, this is paramount.

Upvotes: 2

SergeyA
SergeyA

Reputation: 62583

Since StringBuffer is thread safe, you do not need to have another synchronization point around it (this is what synchronized would do).

Upvotes: 0

Related Questions