Sunday, 26 February 2012

Is StringBuffer really thread-safe ?

May I hope that you have gone through  Difference between StringBuffer and StringBuilder before reading this topic.

All the public methods within StringBuffer are synchronized, which means a method invocation on a StringBuffer instance acts as an atomic operation.i.e.a method execution by one thread can't be interrupted by another thread.

But one point to remember is that,two or more sequential method invocations on a StringBuffer instance by a thread is not synchronized i.e. it is not an atomic operation.

Consider one example


public class Main {
   
    public static void main(String[] args) throws InterruptedException {
         StringBuffer resource = new StringBuffer();

        MyThread t1 = new MyThread(resource);
        t1.setName("t1");
        MyThread t2 = new MyThread(resource);
        t2.setName("t2");
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(resource);
    }
}

class MyThread extends Thread {
    StringBuffer resource;

    MyThread(StringBuffer resource) {
        this.resource = resource;
    }

    public void run() {
        resource.append(Thread.currentThread().getName() + "->1 ");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        resource.append(Thread.currentThread().getName() + "->2 ");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        resource.append(Thread.currentThread().getName() + "->3 ");

    }
}

In the above example,two threads t1 and t2 invoking append method three times sequentially with an interval of 1 second.But you can never confirm that both the threads execute all the three invocation in order .i.e.one thread finishes all the three invocation then the second thread.The order of invocation of method by one thread may be interrupted by another thread.

In the above example,

the expected output is
 
t2->1 t2->2 t2->3 t1->1 t1->2 t1->3
or
t1->1 t1->2 t1->3 t2->1 t2->2 t2->3

But the actual output is

t2->1 t1->1 t1->2 t2->2 t2->3 t1->3

That means "only a single method invocation by a thread is an atomic operation not the multiple method invocations "

In order to make multiple StringBuffer method invocation by a thread in exact order you have to synchronize it externally.See the implementation below

public class Main {

    public static void main(String[] args) throws InterruptedException {
        StringBuffer resource = new StringBuffer();

        MyThread t1 = new MyThread(resource);
        t1.setName("t1");
        MyThread t2 = new MyThread(resource);
        t2.setName("t2");
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(resource);
    }
}

class MyThread extends Thread {
    static Object lock = new Object();
    StringBuffer resource;

    MyThread(StringBuffer resource) {
        this.resource = resource;
    }

    public void run() {
        synchronized (lock) {
            resource.append(Thread.currentThread().getName() + "->1 ");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            resource.append(Thread.currentThread().getName() + "->2 ");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            resource.append(Thread.currentThread().getName() + "->3 ");
        }
    }
}

Here you always get the output as

t1->1 t1->2 t1->3 t2->1 t2->2 t2->3
or
t2->1 t2->2 t2->3 t1->1 t1->2 t1->3


The links that may help you

Difference between volatile and synchronized in Java

Use of synchronization with examples in Java

No comments:

Post a Comment