WalzoneInterview Prep
πŸ“ž Interviewing soon? Practice with a realistic AI mock phone interview β€” it calls you, then scores you. First 15 min FREE β†’

Java Concurrency Β· Guru Β· question 99 of 100

Can you explain the concept of memory fences in Java and how they affect multi-threaded code?

πŸ“• Buy this interview preparation book: 100 Java Concurrency questions & answers β€” PDF + EPUB for $5

In Java, a memory fence is a synchronization mechanism used to control the order of memory operations across threads. The purpose of a memory fence is to ensure that changes made to shared variables by one thread are visible to other threads that access the same variables.

Memory fences are important for maintaining the correctness of multi-threaded programs, as the order in which memory operations occur can be unpredictable due to the non-deterministic nature of thread scheduling.

There are two types of memory fences in Java: acquire and release fences. An acquire fence ensures that memory operations that occur before the fence are visible to the current thread, while a release fence ensures that memory operations that occur after the fence are visible to other threads.

In Java, memory fences can be implemented using the volatile keyword, which ensures that changes made to a variable are visible to other threads immediately. Alternatively, the java.util.concurrent package provides several classes and interfaces for working with memory fences, such as AtomicInteger, AtomicLong, and AtomicReference.

Here is an example of using the AtomicInteger class to implement a counter that can be safely shared across multiple threads:

import java.util.concurrent.atomic.AtomicInteger;

public class Counter {
    private AtomicInteger count = new AtomicInteger(0);
    
    public int getCount() {
        return count.get();
    }
    
    public void increment() {
        count.incrementAndGet();
    }
}

In this example, the count variable is declared as an AtomicInteger, which provides atomic operations for incrementing and getting the current value of the counter. The incrementAndGet() method ensures that the increment operation is atomic and visible to other threads immediately.

Java also provides a java.util.concurrent.locks.Lock interface for controlling access to shared resources. A lock is a synchronization mechanism that allows only one thread to access a shared resource at a time. The Lock interface provides methods for acquiring and releasing the lock, and also provides support for fairness, which ensures that threads acquire the lock in the order they request it.

Here is an example of using the Lock interface to implement a counter that can be safely shared across multiple threads:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Counter {
    private int count = 0;
    private Lock lock = new ReentrantLock();
    
    public int getCount() {
        lock.lock();
        try {
            return count;
        } finally {
            lock.unlock();
        }
    }
    
    public void increment() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }
}

In this example, the count variable is guarded by a Lock object, which ensures that only one thread can access the variable at a time. The lock() and unlock() methods are used to acquire and release the lock, respectively. The try-finally block ensures that the lock is released even if an exception is thrown.

Reading is step one. Saying it out loud is the interview. Our AI interviewer calls your phone and runs a realistic Java Concurrency interview β€” then scores it.
πŸ“ž Practice Java Concurrency β€” free 15 min
πŸ“• Buy this interview preparation book: 100 Java Concurrency questions & answers β€” PDF + EPUB for $5

All 100 Java Concurrency questions Β· All topics