Java
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented Java
-
Section 4: Collections Framework
-
Section 5: Exception Handling
-
Section 6: Generics
-
Section 7: Functional Java
-
Section 8: Concurrency
-
Section 9: I/O and NIO
-
Section 10: JVM Internals
-
Section 11: Modern Java Features
-
Section 12: Build Tools and Project Structure
-
Section 13: Testing
-
Section 14: Databases and Persistence
-
Section 15: Networking
-
Section 16: Design and Best Practices
-
Section 17: Reflection and Annotations
-
Section 18: Logging and Diagnostics
-
Section 19: Date, Time, and Internationalization
-
Section 20: Java Platform Module System
-
Section 21: Security in Java
-
Section 22: Advanced Collections and Data Structures
-
Section 23: More Concurrency Patterns
-
Section 24: Compression, Files, and System Integration
-
Section 25: GUI Programming
-
Section 26: Practical Projects
-
Section 27: Data Structures and Algorithms
-
Section 28: Interview and Algorithm Practice
-
Section 29: JSON and Data Interchange
-
Section 30: More Concurrency Utilities
-
Section 31: More Collections and Streams Practice
-
Section 32: More File and System Programming
-
Section 33: Standard Library Deep Dive
-
Section 34: More Practice and Drills
-
Section 35: More Testing and Quality
-
Section 36: More Design Patterns and Architecture
-
Section 37: Career and Ecosystem
-
Section 38: More OOP and Architecture Practice
-
Section 39: More Enterprise Concepts
-
Section 40: Advanced JavaFX
-
Section 41: More Interview Practice
269: File Locking in Java
Imagine you and a coworker share a single physical logbook on a desk to track office deliveries. If you both grab the book at the exact same time and start scribbling on the same line, you're going to end up with a smeared, unreadable mess of ink. To prevent this, you have a simple rule: whoever is writing in the book holds a heavy metal paperweight on the page. If the paperweight is there, the other person knows to wait their turn. Once the writer is done, they remove the paperweight, signaling that the book is free.
In Java, FileLock is that paperweight. When your application is dealing with files that might be accessed by other processes—or even other instances of your own app running simultaneously—you can't just assume the file is yours. If two processes write to the same byte offset at once, you get data corruption. We use FileChannel to request a lock from the operating system, ensuring that your process has exclusive (or shared) access before a single byte is modified.
Grabbing the Keys with FileChannel
You can't lock a file using a simple FileWriter or FileOutputStream. You need a FileChannel. I usually prefer using RandomAccessFile or FileChannel.open() because they give me the granular control needed for locking.
There are two ways to handle this: lock() and tryLock(). The lock() method is patient—it will block your thread and wait indefinitely until the lock becomes available. tryLock() is for when you're in a hurry; it returns null immediately if another process already holds the lock, allowing your code to do something else instead of just hanging.
import java.io.*;
import java.nio.channels.*;
public class LockExample {
public static void main(String[] args) {
// We use RandomAccessFile to get a channel that supports both reading and writing
try (RandomAccessFile raf = new RandomAccessFile("app_state.dat", "rw");
FileChannel channel = raf.getChannel()) {
System.out.println("Attempting to acquire lock...");
// tryLock() returns null if the lock is held by another process
FileLock lock = channel.tryLock();
if (lock != null) {
try {
System.out.println("Lock acquired! Writing data...");
raf.writeUTF("Update: System Stable at " + System.currentTimeMillis());
// Simulate some heavy work
Thread.sleep(5000);
} finally {
lock.release();
System.out.println("Lock released.");
}
} else {
System.out.println("Another process is using this file. Try again later.");
}
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}
Shared vs. Exclusive Locks
Not every lock needs to be a "keep out" sign. Sometimes, you just want to make sure nobody changes the file while you're reading it, but you don't mind if ten other people are reading it too. This is the difference between a shared lock and an exclusive lock.
- Exclusive Lock: This is the default. Only one process can hold it. No one else can read or write. This is what you use for updating a configuration file or a database header.
- Shared Lock: Multiple processes can hold shared locks simultaneously. However, if anyone wants an exclusive lock to write to the file, they have to wait until all shared locks are released.
To get a shared lock, you pass extra arguments to tryLock(position, size, shared). I've found that using shared locks for "read-only" modes significantly improves performance in apps where reads are frequent but writes are rare.
The Reality of OS Dependencies
Here is a bit of "real world" advice: FileLock is a wrapper around the underlying operating system's locking mechanism. This means it behaves differently depending on whether you're on Windows or Linux.
On some systems, locks are advisory. This means the OS doesn't actually stop another program from writing to the file if that program simply ignores the locking API. It's like a "Do Not Disturb" sign on a hotel door—most people respect it, but someone could still walk right in if they didn't check the door first. Always design your system assuming that some rogue process might ignore your locks.
📋 Practical Task
Build a Concurrent Transaction Logger
Your task is to create a program that simulates multiple instances of an application trying to write a transaction record to a shared file called transactions.txt.
Requirements:
- Create a class that attempts to write a unique transaction ID (e.g., "TXN-123") to
transactions.txt. - Use
FileChannel.tryLock()to ensure that only one instance of your program can write to the file at a time. - If the lock is acquired, write the transaction ID and a timestamp, then hold the lock for 2 seconds (using
Thread.sleep()) to simulate a slow write process. - If the lock is not acquired, print a message: "Conflict detected: File is locked by another process. Transaction deferred."
- To test this, run two instances of your
mainmethod simultaneously in your IDE. You should see one instance successfully writing while the other reports a conflict.
There are no comments for now.