0

I'm not sure if I should synchronize method methodOne() in my example. I think not but I'm not 100% sure. Could you please give me advice what to do?

public class SynchroIssue {

class Test {
    private double a = 0;

    void methodOne() { 
        a++;
    }

    void go() {
        new Thread(new Runnable() {

            @Override
            public void run() {
                for (int i = 0; i < Integer.MAX_VALUE; i++) {
                    methodOne();
                    System.out.println(Thread.currentThread().getName() + ", a = " + a);
                }
            }
        }).start();
    }
}

public static void main(String... args) {
    SynchroIssue mainObj = new SynchroIssue();

    SynchroIssue.Test object1 = mainObj.new Test();
    SynchroIssue.Test object2 = mainObj.new Test();

    object1.go();
    object2.go();
}
}
marekk
  • 41
  • 4

5 Answers5

4

Assuming that you are actually going to use instances of the SynchroIssue class concurrently, which you are not doing currently, the answer is yes.

The increment operator is not atomic. It is actually 3 instructions:

  1. Get the current value.

  2. Add 1 to that.

  3. Store new value.

If you are not synchronized, concurrent threads can overlap those steps resulting in strange behavior.

Another option, if you are truly only interested in integers, would be the use of AtomicInteger, which has methods to atomically increment.

Brett Okken
  • 6,210
  • 1
  • 19
  • 25
  • AtomicInteger for the win. If you are not synchronized, and `a` is not volatile, then the results are even more unpredictable, I think (updates not properly shared between threads, it may jump back and forth). But I don't understand `volatile` in detail, and I don't have to, thanks to higher-level constructs like AtomicInteger. – Thilo Jan 28 '16 at 13:13
1

object1 and object2 are different objects, each start only one thread, and the variable "a" is private and not static, so "a" are different objects too, and there is no interaction between threads. So there is no need to synchronise methodOne().

Tilman Hausherr
  • 17,731
  • 7
  • 58
  • 97
1

In this specific example there's no value to be gained by synchronising the methods because only a single thread ever actually interacts with a given instance.

If you called object1.go() twice you'd have a problem. Using synchronized would not be the best solution to that problem though, you should instead use a java.util.concurrent.atomic.DoubleAccumulator, although AtomicInteger would function just as well given that you start at 0 and only ever increment by 1.

In general, you should be wary of using synchronized to roll your own synchronisation protocols. Prefer instead known thread-safe classes where they're available. java.util.concurrent is a good place to look.

sisyphus
  • 6,174
  • 1
  • 25
  • 35
1

You should, but it wouldn't solve your problem. If you would synchronize the method, only one thread would be able to increase the variable at a time. But the following System.out.println could still print another value, since by the time you call it, another thread may already have increased a. The solution for your problem would be, that methodOne would also have to return the variable. Something like this:

synchronized double methodOne() { 
    return ++a;
}

And the thread should do:

for (int i = 0; i < Integer.MAX_VALUE; i++) {
            System.out.println(Thread.currentThread().getName() + ", a = " + methodOne());
    }

EDIT: as others already pointed out, you only have to do this if you intend to make the variable static. Otherwise you can leave your code as it is.

ZetQ
  • 51
  • 4
0

I want to add some hints to Brett Okken's answer:

  1. Most of the times, when you have a member variable in your class which is modified by the methods of your class in a concurrent context by more than one thread, you should think about one of the synchronization scopes.

  2. Always go for the smallest available scope of synchronization.

Hope this would be helpful.

STaefi
  • 4,297
  • 1
  • 25
  • 43