Article Directory
foreword
In application development, especially web engineering development, it is usually concurrent programming, either multi-process or multi-thread. In this scenario, thread concurrency security issues are prone to occur, and locks have to be used to solve the problem. In multi-threaded high-concurrency scenarios, in order to ensure the thread safety of resources, jdk provides us with synchronized keywords and ReentrantLock reentrant locks, but they can only guarantee thread safety within a project. At present, when distributed clusters, microservices, and cloud native are rampant, how to ensure the thread safety of different processes, services, and machines, jdk does not provide us with existing solutions. At this point, we have to implement it manually with the help of related technologies. The current mainstream implementation methods are as follows:
- Based on mysql relational implementation
- Realization based on redis non-relational data
- Implementation based on zookeeper/etcd
Lock classification
Pessimistic lock : With strong exclusive and exclusive characteristics, the data is locked during the entire data processing process. It is suitable for more writes and will block read operations.
Optimistic locking : A more relaxed locking mechanism is adopted, mostly based on data version (Version) and timestamp. . It is suitable for reading more and will not block reading
Exclusive locks, mutex locks, exclusive locks : Guaranteed to be exclusively and exclusively held by only one thread at any time. synchronized, ReentrantLock
shared lock : can be shared and held by multiple threads at the same time. CountDownLatch countdown counter, Semaphore semaphore
Reentrant lock : also known as recursive lock. When the same thread acquires the lock in the outer method, it will automatically acquire the lock when entering the inner method.
Non-reentrant locks :
Fair locks : locks with priority, first come, first served, whoever applies for the lock first will get the lock first
Unfair lock : locks without priority, latecomers also have the opportunity to get the lock first
Spin lock : When a thread fails to acquire a lock (the lock is already occupied by other threads), an infinite loop retries to try to acquire a lock .
Blocking lock : When a thread fails to acquire a lock, the thread enters a blocked state until it is awakened after receiving a signal . Higher performance in highly competitive situations
Read lock : shared lock
Write lock : exclusive exclusive lock
Biased lock : always accessed by a thread, then the thread will automatically acquire the lock
Lightweight lock (CAS): when the lock is a biased lock, accessed by another thread, the biased lock will be upgraded to a lightweight lock ,
other threads will try to acquire the lock in the form of spin, which will not block and improve performance.
Heavyweight lock : When the lock is a lightweight lock, although another thread is spinning, the spin will not continue forever. When the spin is a certain number of times (10 times),
the lock has not been acquired yet It will enter blocking, and the lock will expand to a heavyweight lock. Heavyweight locks will cause the thread he applied for to block and reduce performance.
The above is actually a synchronized lock upgrade process
Table-level lock : lock the entire table, fast locking and low overhead, no deadlocks, but low concurrency, which will increase the probability of lock conflicts
Row-level lock : the smallest granularity lock in mysql, only for operation rows, It can greatly reduce the probability of lock conflicts, with high concurrency, but slow locking, high overhead, and deadlocks
Redisson
Redisson
It is a Java in-memory data grid (In-Memory Data Grid) implemented on the basis of Redis. It not only provides a series of distributed common Java objects, but also provides many distributed services. These include (BitSet, Set, Multimap, SortedSet, Map, List, Queue, BlockingQueue, Deque, BlockingDeque, Semaphore, Lock, AtomicLong, CountDownLatch, Publish / Subscribe, Bloom filter, Remote service, Spring cache, Executor service, Live Object service , Scheduler service) Redisson provides the easiest and most convenient way to use Redis. The purpose of Redisson is to promote users' separation of concerns about Redis (Separation of Concern), so that users can focus more on processing business logic.
Redisson
The bottom layer is the Netty
framework. Redis 2.8
The above version is supported, the above Java1.6+
version is supported.
Reentrant Lock
The Redisson distributed reentrant lock RLock
Java object based on Redis implements the java.util.concurrent.locks.Lock
interface.
If the Redisson node responsible for storing the distributed lock goes down and the lock happens to be locked, the lock will appear locked. In order to avoid this situation, Redisson internally provides a watchdog (timer timer) that monitors the lock. Its function is to continuously extend the validity period of the lock before the Redisson instance is closed. By default, the timeout period for the watchdog to check the lock is 30 seconds, and it can also be modified Config.lockWatchdogTimeout
to specify otherwise.
RLock
The object fully conforms to Java's Lock specification. That is to say, only the process that owns the lock can unlock it, and an error will be thrown if other processes unlock it IllegalMonitorStateException
.
In addition, Redisson also leaseTime
specifies the locking time through the parameters provided by the locking method. After exceeding this time, the lock will be unlocked automatically.
RLock lock = redisson.getLock("anyLock");
// 最常见的使用方法
lock.lock();
// 加锁以后10秒钟自动解锁
// 无需调用unlock方法手动解锁
lock.lock(10, TimeUnit.SECONDS);
// 尝试加锁,最多等待100秒,上锁以后10秒自动解锁
boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
if (res) {
try {
...
} finally {
lock.unlock();
}
}
1. Introduce dependencies
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.11.2</version>
</dependency>
2. Add configuration
@Configuration
public class RedissonConfig {
@Bean
public RedissonClient redissonClient(){
Config config = new Config();
// 可以用"rediss://"来启用SSL连接
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
return Redisson.create(config);
}
}
3. Use in the code
@Autowired
private RedissonClient redissonClient;
public void checkAndLock() {
// 加锁,获取锁失败重试
RLock lock = this.redissonClient.getLock("lock");
lock.lock();
// todo 一些处理业务
// 释放锁
lock.unlock();
}
Fair Lock (Fair Lock)
The Redisson distributed reentrant fair lock based on Redis is also java.util.concurrent.locks.Lock
an RLock
object that implements the interface. At the same time, it also provides asynchronous (Async) , reflective (Reactive) and RxJava2 standard interfaces. It ensures that when multiple Redisson client threads request locks at the same time, they are assigned to the thread that made the request first. All request threads will be queued in a queue. When a thread goes down, Redisson will wait for 5 seconds before continuing to the next thread. That is to say, if there are 5 threads in the front waiting state, then the following thread will wait At least 25 seconds.
Reentrant locks are based on luck, and the first request may not be the first to grab the lock
. Fair locks are based on network speed and hand speed, and there will be a queue inside to ensure the order
RLock fairLock = redisson.getFairLock("anyLock");
// 最常见的使用方法
fairLock.lock();
// 10秒钟以后自动解锁
// 无需调用unlock方法手动解锁
fairLock.lock(10, TimeUnit.SECONDS);
// 尝试加锁,最多等待100秒,上锁以后10秒自动解锁
boolean res = fairLock.tryLock(100, 10, TimeUnit.SECONDS);
fairLock.unlock();
Interlock (MultiLock)
Redis-based Redisson distributed interlocking RedissonMultiLock
objects can RLock
associate multiple objects as an interlocking, and each RLock
object instance can come from a different Redisson instance.
RLock lock1 = redissonInstance1.getLock("lock1");
RLock lock2 = redissonInstance2.getLock("lock2");
RLock lock3 = redissonInstance3.getLock("lock3");
RedissonMultiLock lock = new RedissonMultiLock(lock1, lock2, lock3);
// 同时加锁:lock1 lock2 lock3
// 所有的锁都上锁成功才算成功。
lock.lock();
...
lock.unlock();
Red Lock (RedLock)
The Redisson red lock RedissonRedLock
object based on Redis implements the locking algorithm introduced by Redlock . This object can also be used to associate multiple RLock
objects as a red lock, and each RLock
object instance can come from a different Redisson instance.
RLock lock1 = redissonInstance1.getLock("lock1");
RLock lock2 = redissonInstance2.getLock("lock2");
RLock lock3 = redissonInstance3.getLock("lock3");
RedissonRedLock lock = new RedissonRedLock(lock1, lock2, lock3);
// 同时加锁:lock1 lock2 lock3
// 红锁在大部分节点上加锁成功就算成功。
lock.lock();
...
lock.unlock();
ReadWriteLock (ReadWriteLock)
The Redisson distributed reentrant read-write lock RReadWriteLock
Java object based on Redis implements the java.util.concurrent.locks.ReadWriteLock
interface. Among them, both the read lock and the write lock inherit the RLock interface.
Distributed reentrant read-write locks allow multiple read locks and one write lock to be locked at the same time.
RReadWriteLock rwlock = redisson.getReadWriteLock("anyRWLock");
// 最常见的使用方法
rwlock.readLock().lock();
// 或
rwlock.writeLock().lock();
// 10秒钟以后自动解锁
// 无需调用unlock方法手动解锁
rwlock.readLock().lock(10, TimeUnit.SECONDS);
// 或
rwlock.writeLock().lock(10, TimeUnit.SECONDS);
// 尝试加锁,最多等待100秒,上锁以后10秒自动解锁
boolean res = rwlock.readLock().tryLock(100, 10, TimeUnit.SECONDS);
// 或
boolean res = rwlock.writeLock().tryLock(100, 10, TimeUnit.SECONDS);
...
lock.unlock();
concurrent operation | Can it be concurrent |
---|---|
write and write | cannot be concurrent |
read and write | cannot be concurrent |
read and read | Can be concurrent |
Semaphore
Redisson's distributed semaphore ( Semaphore ) Java object based on Redis RSemaphore
adopts a java.util.concurrent.Semaphore
similar interface and usage. At the same time, it also provides asynchronous (Async) , reflective (Reactive) and RxJava2 standard interfaces.
RSemaphore semaphore = redisson.getSemaphore("semaphore");
semaphore.trySetPermits(3);
//尝试获取许可,如果成功就会继续执行,否则就会被阻塞
semaphore.acquire();
//释放许可供其他线程使用,之前被阻塞的线程会被唤醒继续执行
semaphore.release();
Semaphore
It is generally used for traffic control, especially in application scenarios with limited public resources. For example, for database connections, assuming that the number of database connections is 10, multiple threads can use Semaphore to control the number of concurrent database threads up to 10.
For example, there are only three parking spaces in the parking lot of a shopping mall, and there are ten cars outside that need to come in. At this time, it must be ensured that only three cars can enter the parking lot at the same time, and other cars can only drive in when there are empty parking spaces.
Latch (CountDownLatch)
The Redisson distributed lock ( CountDownLatch ) Java object based on Redisson RCountDownLatch
adopts a java.util.concurrent.CountDownLatch
similar interface and usage.
RCountDownLatch latch = redisson.getCountDownLatch("anyCountDownLatch");
latch.trySetCount(1);
latch.await();
// 在其他线程或其他JVM里
RCountDownLatch latch = redisson.getCountDownLatch("anyCountDownLatch");
latch.countDown();
JUC
The CountDownLatch
role in can only be limited to a single JVM, once the cluster is deployed, it cannot be guaranteed