In concurrent programmnig, a shared counter must be thread-safe to prevent data races and inconsistencies when accessed by multiple threads simultaneously. Two primary approaches to achieve this are using atomic variables and explicit synchronization via locks.
Using AtomicInteger
AtomicInteger provides atomic operations without requiring explicit locking, utilizing low-level CPU instructions for thread safety.
public class AtomicBasedCounter implements Runnable {
private static final AtomicInteger sharedCounter = new AtomicInteger(0);
public static void performIncrement() {
if (sharedCounter.get() < 1000) {
int updatedValue = sharedCounter.incrementAndGet();
System.out.println("Thread " + Thread.currentThread().getName() + " processed count: " + updatedValue);
}
}
@Override
public void run() {
while (true) {
performIncrement();
try {
Thread.sleep(0);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
for (int i = 0; i < 6; i++) {
AtomicBasedCounter task = new AtomicBasedCounter();
new Thread(task).start();
}
}
}
Using Synchronized Methods
The synchronized keyword ensures that only one thread can execute the method at a time, porviding mutual exclusion.
public class SynchronizedCounter {
private int value = 0;
public synchronized void increase() {
value++;
System.out.println("Thread " + Thread.currentThread().getName() + " updated value to: " + value);
}
public static void main(String[] args) {
SynchronizedCounter counterInstance = new SynchronizedCounter();
for (int i = 0; i < 5; i++) {
new Thread(() -> {
for (int j = 0; j < 100; j++) {
counterInstance.increase();
try {
Thread.sleep(0);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
}