class ThreadSafeClass extends Thread
{
private static int count = 0;
public synchronized static void increment()
{
count++;
}
public synchronized void decrement()
{
count--;
}
}
Can anyone explain why above class is not thread safe?
When designing a class that may be used for concurrent programming—that is, a class whose instances may be used by more than one thread at a time—it is imperative that you make sure the class is " thread-safe.” Consider the IntList class of Example 2-7. This class is not thread safe.
For a standard Java SE class, the best way to know whether or not the class is thread-safe is to carefully read its documentation. Always read both the class documentation and the method documentation. If either say it's not synchronized or not thread-safe, you know it's not thread-safe.
Not thread safe: Data structures should not be accessed simultaneously by different threads.
When multiple threads are working on the same data, and the value of our data is changing, that scenario is not thread-safe and we will get inconsistent results. When a thread is already working on an object and preventing another thread on working on the same object, this process is called Thread-Safety.
Thread-Safe Classes - Java Examples in a Nutshell, 3rd Edition [Book] When designing a class that may be used for concurrent programming—that is, a class whose instances may be used by more than one thread at a time—it is imperative that you make sure the class is " thread-safe.” Consider the IntList class of Example 2-7.
To make these classes thread-safe, you must prevent concurrent access to the internal state of an instance by more than one thread. Because Java was designed with threads in mind, the language provides the synchronized modifier, which does just that.
As we know Java has a feature, Multithreading, which is a process of running multiple threads simultaneously. When multiple threads are working on the same data, and the value of our data is changing, that scenario is not thread-safe and we will get inconsistent results.
Although multithreading is a powerful feature, it comes at a price. In multithreaded environments, we need to write implementations in a thread-safe way. This means that different threads can access the same resources without exposing erroneous behavior or producing unpredictable results. This programming methodology is known as “thread-safety”.
Since the increment
method is static
it will synchronize on the class object for the ThreadSafeClass
. The decrement
method is not static and will synchronize on the instance used to call it. I.e., they will synchronize on different objects and thus two different threads can execute the methods at the same time. Since the ++
and --
operations are not atomic the class is not thread safe.
Also, since count
is static
, modifying it from decrement
which is a synchronized instance method is unsafe since it can be called on different instances and modify count
concurrently that way.
You have two synchronized methods, but one of them is static and the other is not. When accessing a synchronized method, based on it's type (static or non-static), a different object will be locked. For a static method, a lock will be put on the Class object, while for the non-static block, a lock will be put on the instance of the class that runs the method. Because you have two different locked objects, you can have two threads that modify the same object simultaneously.
Can anyone explain why above class is not thread safe?
increment
being static, synchronization will be done on the class itself.decrement
being not static, synchronization will be done on the object instantiation, but that doesn't secure anything as count
is static.I'd like to add that to declare a thread-safe counter, I believe the simplest way is to use AtomicInteger
instead of a primitive int.
Let me redirect you to the java.util.concurrent.atomic
package-info.
Others' answers are pretty good explained the reason. I just add something to summarize synchronized
:
public class A {
public synchronized void fun1() {}
public synchronized void fun2() {}
public void fun3() {}
public static synchronized void fun4() {}
public static void fun5() {}
}
A a1 = new A();
synchronized
on fun1
and fun2
is synchronized on instance object level. synchronized
on fun4
is synchronized on class object level. Which means:
a1.fun1()
at same time, latter call will be blocked.a1.fun1()
and thread 2 call a1.fun2()
at same time, latter call will be blocked.a1.fun1()
and thread 2 call a1.fun3()
at same time, no blocking, the 2 methods will be executed at same time.A.fun4()
, if other threads call A.fun4()
or A.fun5()
at same time, latter calls will be blocked since synchronized
on fun4
is class level.A.fun4()
, thread 2 call a1.fun1()
at same time, no blocking, the 2 methods will be executed at same time.decrement
is locking on a different thing to increment
so they do not prevent each other from running.decrement
on one instance is locking on a different thing to calling decrement
on another instance, but they are affecting the same thing.The first means that overlapping calls to increment
and decrement
could result in a cancel-out (correct), an increment or a decrement.
The second means that two overlapping calls to decrement
on different instances could result in a double decrement (correct) or a single decrement.
Since two different methods, one is instance level and other is class level, so you need to lock on 2 different objects to make it ThreadSafe
As explained in other answers, your code is not Thread safe since static method increment()
locks Class monitor and non-static method decrement()
locks Object monitor.
For this code example, better solution exists without synchronzed
keyword usage.
You have to use AtomicInteger to achieve Thread safety.
Thread safe using AtomicInteger
:
import java.util.concurrent.atomic.AtomicInteger;
class ThreadSafeClass extends Thread {
private static AtomicInteger count = new AtomicInteger(0);
public static void increment() {
count.incrementAndGet();
}
public static void decrement() {
count.decrementAndGet();
}
public static int value() {
return count.get();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With