Unravel โบ Java concepts โบ Concurrency
Distributed locks and job locking (ShedLock)
When several copies of an application run at once, an ordinary synchronized or ReentrantLock only protects one copy.
Learn these first
At a glance
One meeting-room key kept at the front desk instead of a sticky note on each door.
- 1ShedLock wraps scheduled methods: @SchedulerLock(name = "nightlyReport", lockAtMostFor = "10m") makes sure that across all instances only the one that grabs the lock row or Redis key runs it
- 2Redisson (RLock), Spring Integration (LockRegistry) and Curator (ZooKeeper) offer a lock object: lock.tryLock(...), work, unlock() in a finally block
- 3Every distributed lock has an expiry (lease time) so that a crashed holder does not block everyone forever
- 4Database advisory locks and SELECT ..FOR UPDATE are simpler options when the shared database is the resource being protected.
- A lease can expire while the holder is still working (a long GC pause or slow call)
- lockAtMostFor must be longer than the job ever takes and lockAtLeastFor stops a fast job from running again on a node with a slightly different clock
- Redis-based locks use SET key value NX PX ttl (set if not exists with expiry) and release with a Lua script that checks the stored owner token, so one holder cannot delete another lock
RLock lock = redisson.getLock("invoice-run");if (lock.tryLock(0, 30, TimeUnit.MINUTES)) { try { invoiceService.runMonthly(); } finally { lock.unlock(); }}- line 1 uses RLock, the core of Distributed locks and job locking (ShedLock)
Read the full explanation (about 2 min)
When several copies of an application run at once, an ordinary synchronized or ReentrantLock only protects one copy. A distributed lock uses a shared store (database, Redis, ZooKeeper) so that only one instance at a time runs a job or touches a resource.
A mental model
One meeting-room key kept at the front desk instead of a sticky note on each door. Everybody in the building has to ask the desk, so two teams cannot both believe the room is theirs.
How it works
ShedLock wraps scheduled methods: @SchedulerLock(name = "nightlyReport", lockAtMostFor = "10m") makes sure that across all instances only the one that grabs the lock row or Redis key runs it.
Redisson (RLock), Spring Integration (LockRegistry) and Curator (ZooKeeper) offer a lock object: lock.tryLock(...), work, unlock() in a finally block.
Every distributed lock has an expiry (lease time) so that a crashed holder does not block everyone forever.
Database advisory locks and SELECT ... FOR UPDATE are simpler options when the shared database is the resource being protected.
Going deeper
A lease can expire while the holder is still working (a long GC pause or slow call). Two instances then both believe they hold the lock. For correctness, use fencing tokens or make the work idempotent, and treat the lock as an efficiency measure, not a guarantee.
lockAtMostFor must be longer than the job ever takes and lockAtLeastFor stops a fast job from running again on a node with a slightly different clock.
Clock differences between nodes matter for time-based leases; use the database or Redis server time where possible.
Under the hood
Redis-based locks use SET key value NX PX ttl (set if not exists with expiry) and release with a Lua script that checks the stored owner token, so one holder cannot delete another lock. JDBC providers insert or update a row guarded by a unique key with a lock-until timestamp.
Common mistakes
- Forgetting
unlock()in a finally block. - Lease shorter than the job duration.
- Relying on a lock for correctness without idempotent work.
- In-JVM locks for multi-instance deployments.
How Unravel recognises it
When you paste code, Unravel looks for these signs of distributed locks and job locking (shedlock). Spotting them yourself is a good habit when you read unfamiliar code:
- Annotations:
@EnableSchedulerLock - Types:
LockProviderLockConfigurationLockingTaskExecutorJdbcTemplateLockProviderRedisLockProviderLockAssertRedissonClientRLockRedisLockRegistryLockRegistryJdbcLockRegistryInterProcessMutexCuratorFramework - Method calls:
pg_advisory_lock()tryAdvisoryLock()
Part of a learning path
- Scheduled jobs, batch and integration ยท previous: Quartz scheduler ยท next: Spring Batch jobs, steps and chunks
Related concepts
Frequently asked questions
What is Distributed locks and job locking (ShedLock)?
When several copies of an application run at once, an ordinary synchronized or ReentrantLock only protects one copy. A distributed lock uses a shared store (database, Redis, ZooKeeper) so that only one instance at a time runs a job or touches a resource.
What are the most common mistakes with Distributed locks and job locking (ShedLock)?
Forgetting unlock() in a finally block. Lease shorter than the job duration. Relying on a lock for correctness without idempotent work. In-JVM locks for multi-instance deployments.
How can I see Distributed locks and job locking (ShedLock) in real code?
Paste your own Java into Unravel and it will point out where distributed locks and job locking (shedlock) appears, explain how it works at the detail level you choose, and draw the flow of the code around it.