代码拉取完成,页面将自动刷新
原来的Synchronized + Lock只能锁单机,也就是只能在一个JVM环境下
而在分布式+集群的环境下,变成了N对N的关系,在并发的环境下,如果使用UUID或者自增ID,就可能出现ID重复的问题,因此在集群下的环境下,对JVM进行加锁,这就是分布式锁。
分布式锁 | 优点 | 缺点 |
---|---|---|
Zookeeper | 1.有封装好的框架,容易实现 2.有等待锁的队列,大大提升抢锁效率。 | 添加和删除节点性能较低 |
Redis | Set和Del指令性能较高 | 1.实现复杂,需要考虑超时,原子性,误删等情形。2.没有等待锁的队列,只能在客户端自旋来等待,效率低下。 |
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>distributedLocker</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.11.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
</dependencies>
</project>
spring:
redis:
database: 1
host: localhost
port: 6379
server:
port: 8081
public interface DistributedLocker {
/**
* 获取锁,并且加锁
* 拿不到lock就不罢休,不然线程就一直block
* @param lockKey 锁名
* @return
*/
RLock lock(String lockKey);
/**
* 获取锁,并且加锁
* 拿不到lock就不罢休,不然线程就一直block
* @param lockKey 锁名
* @param timeout 超时时间-> 单位 SECONDS
* @return
*/
RLock lock(String lockKey, long timeout);
/**
* 获取锁,并且加锁
* 拿不到lock就不罢休,不然线程就一直block
* @param lockKey 锁名
* @param unit 时间单位
* @param timeout 超时时间
* @return
*/
RLock lock(String lockKey, TimeUnit unit, long timeout);
/**
* 在 waitTime 时间内 尝试获取锁,并加锁,超过waitTime 将结束等待 返回false
* @param lockKey 锁名
* @param unit 时间单位
* @param waitTime 等待时间
* @param leaseTime 锁过期时间,如果超过leaseTime还没有解锁的话, 我就强制解锁
* @return
*/
boolean tryLock(String lockKey, TimeUnit unit, long waitTime, long leaseTime);
/**
* 在 waitTime(默认5秒) 时间内 尝试获取锁,并加锁,超过waitTime(10秒) 将结束等待 返回false
* @param lockKey
* @return
*/
boolean tryLock(String lockKey);
/**
* 解锁
* @param lockKey
*/
void unlock(String lockKey);
/**
* 解锁
* @param lock
*/
void unlock(RLock lock);
}
@Component
public class RedissonDistributedLocker implements DistributedLocker{
@Autowired
private RedissonClient redissonClient;
/**
* 获取锁,并且加锁
* 拿不到lock就不罢休,不然线程就一直block
* @param lockKey 锁名
* @return
*/
@Override
public RLock lock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock();
return lock;
}
/**
* 获取锁,并且加锁
* 拿不到lock就不罢休,不然线程就一直block
* @param lockKey 锁名
* @param timeout 超时时间-> 单位 SECONDS
* @return
*/
@Override
public RLock lock(String lockKey, long timeout) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock(timeout, TimeUnit.SECONDS);
return lock;
}
/**
* 获取锁,并且加锁
* 拿不到lock就不罢休,不然线程就一直block
* @param lockKey 锁名
* @param unit 时间单位
* @param timeout 超时时间
* @return
*/
@Override
public RLock lock(String lockKey, TimeUnit unit, long timeout) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock(timeout, unit);
return lock;
}
/**
* 在 waitTime 时间内 尝试获取锁,并加锁,超过waitTime 将结束等待 返回false
* @param lockKey 锁名
* @param unit 时间单位
* @param waitTime 等待时间
* @param leaseTime 锁过期时间,如果超过leaseTime还没有解锁的话, 我就强制解锁
* @return
*/
@Override
public boolean tryLock(String lockKey, TimeUnit unit, long waitTime, long leaseTime) {
RLock lock = redissonClient.getLock(lockKey);
try {
return lock.tryLock(waitTime, leaseTime, unit);
} catch (InterruptedException e) {
e.printStackTrace();
return false;
}
}
/**
* 在 waitTime(默认5秒) 时间内 尝试获取锁,并加锁,超过waitTime(10秒) 将结束等待 返回false
* @param lockKey
* @return
*/
@Override
public boolean tryLock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
try {
return lock.tryLock(Constant.R_LOCK_DEFAULT_WAIT_TIME, Constant.R_LOCK_DEFAULT_LEASE_TIME, TimeUnit.SECONDS);
} catch (InterruptedException e) {
return false;
}
}
/**
* 解锁
* @param lockKey
*/
@Override
public void unlock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
lock.unlock();
}
/**
* 解锁
* @param lock
*/
@Override
public void unlock(RLock lock) {
lock.unlock();
}
}
public class Constant {
public static final String R_LOCK = "r_lock_";
/** redis分布式锁 默认等待时间 */
public static final Long R_LOCK_DEFAULT_WAIT_TIME = 5L;
/** redis分布式锁 默认超时时间 */
public static final Long R_LOCK_DEFAULT_LEASE_TIME = 10L;
}
@Configuration
public class RedissonManager {
@Value("${redisson.address}")
private String addressUrl;
// 注意的是,在这个配置类里面,关于redis连接配置还有很多其他参数,比如像连接的用户名、密码、超时时间、连接的库信息等,可以根据需要往里面添加
@Bean
public RedissonClient getRedisson() throws Exception{
RedissonClient redisson = null;
Config config = new Config();
config.useSingleServer()
.setAddress(addressUrl);
redisson = Redisson.create(config);
System.out.println(redisson.getConfig().toJSON().toString());
return redisson;
}
}
@RestController
@RequestMapping("/redisson")
public class RLockTestController {
@Autowired
private DistributedLocker distributedLocker;
@RequestMapping("/test")
public void redissonTest() {
String key = Constant.R_LOCK + "test";
for (int i = 0; i < 100; i++) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
boolean res = false;
try {
System.err.println("=============线程开启============" + Thread.currentThread().getName());
res = distributedLocker.tryLock(key); // 尝试获取锁,等待5秒,自己获得锁后一直不解锁则10秒后自动解锁
if (res) {
System.out.println("线程:" + Thread.currentThread().getName() + ",获取到了锁");
Thread.sleep(100); // 获得锁之后可以进行相应的处理
System.err.println("======获得锁后进行相应的操作======" + Thread.currentThread().getName());
System.err.println("=============================" + Thread.currentThread().getName());
} else {
System.err.println("获取锁失败");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (res) {
distributedLocker.unlock(key);
}
}
}
});
t.start();
}
}
}
http://localhost:8081/redisson/test 控制台输出
=============线程开启============Thread-316
=============线程开启============Thread-317
=============线程开启============Thread-318
=============线程开启============Thread-319
=============线程开启============Thread-320
=============线程开启============Thread-321
=============线程开启============Thread-322
=============线程开启============Thread-323
=============线程开启============Thread-324
=============线程开启============Thread-325
=============线程开启============Thread-326
=============线程开启============Thread-327
=============线程开启============Thread-328
=============线程开启============Thread-329
=============线程开启============Thread-330
=============线程开启============Thread-331
=============线程开启============Thread-332
=============线程开启============Thread-333
=============线程开启============Thread-334
=============线程开启============Thread-335
=============线程开启============Thread-336
=============线程开启============Thread-337
=============线程开启============Thread-338
=============线程开启============Thread-339
=============线程开启============Thread-340
=============线程开启============Thread-341
=============线程开启============Thread-342
=============线程开启============Thread-343
=============线程开启============Thread-344
=============线程开启============Thread-345
=============线程开启============Thread-346
=============线程开启============Thread-347
=============线程开启============Thread-348
=============线程开启============Thread-349
=============线程开启============Thread-350
=============线程开启============Thread-351
=============线程开启============Thread-352
=============线程开启============Thread-353
=============线程开启============Thread-354
=============线程开启============Thread-355
=============线程开启============Thread-356
=============线程开启============Thread-357
=============线程开启============Thread-358
=============线程开启============Thread-359
=============线程开启============Thread-360
=============线程开启============Thread-361
=============线程开启============Thread-362
=============线程开启============Thread-363
=============线程开启============Thread-364
=============线程开启============Thread-365
=============线程开启============Thread-366
=============线程开启============Thread-367
=============线程开启============Thread-368
=============线程开启============Thread-369
=============线程开启============Thread-370
=============线程开启============Thread-371
=============线程开启============Thread-372
=============线程开启============Thread-373
=============线程开启============Thread-374
=============线程开启============Thread-375
=============线程开启============Thread-376
=============线程开启============Thread-377
=============线程开启============Thread-378
=============线程开启============Thread-379
=============线程开启============Thread-380
=============线程开启============Thread-381
=============线程开启============Thread-382
=============线程开启============Thread-383
=============线程开启============Thread-384
=============线程开启============Thread-385
=============线程开启============Thread-386
=============线程开启============Thread-387
=============线程开启============Thread-388
=============线程开启============Thread-389
=============线程开启============Thread-390
=============线程开启============Thread-391
=============线程开启============Thread-392
=============线程开启============Thread-393
=============线程开启============Thread-394
=============线程开启============Thread-395
=============线程开启============Thread-396
=============线程开启============Thread-397
=============线程开启============Thread-398
=============线程开启============Thread-399
=============线程开启============Thread-400
=============线程开启============Thread-401
=============线程开启============Thread-402
=============线程开启============Thread-403
=============线程开启============Thread-404
=============线程开启============Thread-405
=============线程开启============Thread-406
=============线程开启============Thread-407
=============线程开启============Thread-408
=============线程开启============Thread-409
=============线程开启============Thread-410
=============线程开启============Thread-411
=============线程开启============Thread-412
=============线程开启============Thread-413
=============线程开启============Thread-414
=============线程开启============Thread-415
线程:Thread-363,获取到了锁
======获得锁后进行相应的操作======Thread-363
=============================Thread-363
线程:Thread-380,获取到了锁
======获得锁后进行相应的操作======Thread-380
=============================Thread-380
线程:Thread-381,获取到了锁
======获得锁后进行相应的操作======Thread-381
=============================Thread-381
线程:Thread-324,获取到了锁
======获得锁后进行相应的操作======Thread-324
=============================Thread-324
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
获取锁失败
安装zookeeper 请参考安装zookeeper
多个JVM服务器之间,同时在zookeeper上创建相同一个临时节点,因为临时节点路径是保证唯一,只要谁能创建节点成功,谁就能获取到锁。
没有创建成功节点,只能注册个监听器监听这个锁并进行等待,当释放锁的时候,采用事件通知给其它客户端重新获取锁的资源。
这时候客户端使用事件监听,如果该临时节点被删除的话,重新进入获取锁的步骤。
Zookeeper使用直接关闭临时节点session会话连接,因为临时节点生命周期与session会话绑定在一块,如果session会话连接关闭,该临时节点也会被删除,这时候客户端使用事件监听,如果该临时节点被删除的话,重新进入到获取锁的步骤。
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>4.3.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.3.0</version>
</dependency>
Slf4j
@Data
public class ZKclient {
private CuratorFramework client;
//Zk集群地址
private static final String ZK_ADDRESS = "localhost:2181";
private static final String USER_NAME = "root";
private static final String PASSWORD = "123456";
// private static final String ZK_ADDRESS = "localhot:2181,localhot:2182,localhot:2183";
public static ZKclient instance = null;
static {
instance = new ZKclient();
instance.init();
}
private ZKclient() {
}
public void init() {
if (null != client) {
return;
}
//创建客户端
if (USER_NAME != null && PASSWORD != null) {
client = ClientFactory.create(ZK_ADDRESS, USER_NAME, PASSWORD);
} else {
client = ClientFactory.createSimple(ZK_ADDRESS);
}
//启动客户端实例,连接服务器
client.start();
}
public void destroy() {
CloseableUtils.closeQuietly(client);
}
}
**
* zookeeper 连接实现
**/
public class ClientFactory {
/**
* @param connectionString zk的连接地址
* @return CuratorFramework 实例
*/
public static CuratorFramework createSimple(String connectionString) {
// 重试策略:第一次重试等待1s,第二次重试等待2s,第三次重试等待4s
// 第一个参数:等待时间的基础单位,单位为毫秒
// 第二个参数:最大重试次数
ExponentialBackoffRetry retryPolicy =
new ExponentialBackoffRetry(1000, 3);
// 获取 CuratorFramework 实例的最简单的方式
// 第一个参数:zk的连接地址
// 第二个参数:重试策略
return CuratorFrameworkFactory.newClient(connectionString, retryPolicy);
}
/**
*
* @param zkAddress zk的连接地址
* @param userName 账号
* @param password 密码
* @return
*/
public static CuratorFramework create(String zkAddress, String userName, String password) {
ExponentialBackoffRetry retryPolicy =
new ExponentialBackoffRetry(1000, 3);
return CuratorFrameworkFactory.builder()
.authorization("digest", (userName + ":" + password).getBytes())
.connectString(zkAddress)
.retryPolicy(retryPolicy)
.namespace("zk/lock").build();
}
/**
* @param connectionString zk的连接地址
* @param retryPolicy 重试策略
* @param connectionTimeoutMs 连接
* @param sessionTimeoutMs
* @return CuratorFramework 实例
*/
public static CuratorFramework createWithOptions(
String connectionString, RetryPolicy retryPolicy,
int connectionTimeoutMs, int sessionTimeoutMs) {
// builder 模式创建 CuratorFramework 实例
return CuratorFrameworkFactory.builder()
.connectString(connectionString)
.retryPolicy(retryPolicy)
.connectionTimeoutMs(connectionTimeoutMs)
.sessionTimeoutMs(sessionTimeoutMs)
// 其他的创建选项
.build();
}
}
public interface DistributedZLock {
/**
* 加锁
* 拿不到lock就不罢休,不然线程就一直block
* @param lock 锁
* @return
*/
void lock(InterProcessLock lock);
/**
* 获取锁
* @param lockKey 锁名
* @return
*/
InterProcessLock getLock(String lockKey);
/**
* 在 waitTime 时间内 加锁,超过waitTime 将结束等待 返回false
* @param lock 锁
* @param unit 时间单位
* @param waitTime 等待时间
* @return
*/
boolean tryLock(InterProcessLock lock, TimeUnit unit, long waitTime);
/**
* 在 waitTime(默认5秒) 时间内 尝试加锁,超过waitTime(10秒) 将结束等待 返回false
* @param lock
* @return
*/
boolean tryLock(InterProcessLock lock);
/**
* 解锁
* @param lock
*/
void unlock(InterProcessLock lock);
}
@Component
public class ZookeeperDistributedLock implements DistributedZLock {
@Override
public void lock(InterProcessLock lock) {
try {
lock.acquire();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public InterProcessLock getLock(String lockKey) {
return new InterProcessSemaphoreMutex(ZKclient.instance.getClient(), lockKey);
}
@Override
public boolean tryLock(InterProcessLock lock, TimeUnit unit, long waitTime) {
try {
return lock.acquire(waitTime, unit);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
@Override
public boolean tryLock(InterProcessLock lock) {
try {
lock.acquire(Constant.R_LOCK_DEFAULT_WAIT_TIME, TimeUnit.SECONDS);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
@Override
public void unlock(InterProcessLock lock) {
try {
lock.release();
} catch (Exception e) {
// e.printStackTrace();
System.out.println("解锁失败");
}
}
}
@Test
public void testLock2() throws Exception {
String lockKey = Constant.Z_LOCK + "test";
for (int i = 0; i < 30; i++) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
boolean res = false;
InterProcessLock lock = distributedZLock.getLock(lockKey);
try {
System.err.println("=============线程开启============" + Thread.currentThread().getName());
res = distributedZLock.tryLock(lock); // 尝试获取锁,等待5秒,自己获得锁后一直不解锁则10秒后自动解锁
if (res) {
System.out.println("线程:" + Thread.currentThread().getName() + ",获取到了锁");
Thread.sleep(50); // 获得锁之后可以进行相应的处理
System.err.println("======获得锁后进行相应的操作======" + Thread.currentThread().getName());
System.err.println("=============================" + Thread.currentThread().getName());
} else {
System.err.println("获取锁失败");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (res) {
distributedZLock.unlock(lock);
}
}
}
});
t.start();
}
Thread.sleep(10000);
}
=============线程开启============Thread-8
=============线程开启============Thread-3
=============线程开启============Thread-13
=============线程开启============Thread-30
=============线程开启============Thread-5
=============线程开启============Thread-29
=============线程开启============Thread-14
=============线程开启============Thread-16
=============线程开启============Thread-17
=============线程开启============Thread-24
=============线程开启============Thread-20
=============线程开启============Thread-12
=============线程开启============Thread-11
=============线程开启============Thread-28
=============线程开启============Thread-21
=============线程开启============Thread-9
=============线程开启============Thread-22
=============线程开启============Thread-10
=============线程开启============Thread-4
=============线程开启============Thread-15
=============线程开启============Thread-31
=============线程开启============Thread-6
=============线程开启============Thread-23
=============线程开启============Thread-32
=============线程开启============Thread-19
=============线程开启============Thread-18
=============线程开启============Thread-26
=============线程开启============Thread-27
=============线程开启============Thread-25
=============线程开启============Thread-7
线程:Thread-13,获取到了锁
======获得锁后进行相应的操作======Thread-13
=============================Thread-13
线程:Thread-20,获取到了锁
======获得锁后进行相应的操作======Thread-20
=============================Thread-20
线程:Thread-9,获取到了锁
======获得锁后进行相应的操作======Thread-9
=============================Thread-9
线程:Thread-10,获取到了锁
======获得锁后进行相应的操作======Thread-10
=============================Thread-10
线程:Thread-28,获取到了锁
======获得锁后进行相应的操作======Thread-28
=============================Thread-28
线程:Thread-32,获取到了锁
======获得锁后进行相应的操作======Thread-32
=============================Thread-32
线程:Thread-14,获取到了锁
======获得锁后进行相应的操作======Thread-14
=============================Thread-14
线程:Thread-29,获取到了锁
======获得锁后进行相应的操作======Thread-29
=============================Thread-29
线程:Thread-24,获取到了锁
======获得锁后进行相应的操作======Thread-24
=============================Thread-24
线程:Thread-15,获取到了锁
======获得锁后进行相应的操作======Thread-15
=============================Thread-15
线程:Thread-22,获取到了锁
======获得锁后进行相应的操作======Thread-22
=============================Thread-22
线程:Thread-18,获取到了锁
======获得锁后进行相应的操作======Thread-18
=============================Thread-18
线程:Thread-19,获取到了锁
======获得锁后进行相应的操作======Thread-19
=============================Thread-19
线程:Thread-11,获取到了锁
======获得锁后进行相应的操作======Thread-11
=============================Thread-11
线程:Thread-21,获取到了锁
======获得锁后进行相应的操作======Thread-21
=============================Thread-21
线程:Thread-23,获取到了锁
======获得锁后进行相应的操作======Thread-23
=============================Thread-23
线程:Thread-3,获取到了锁
======获得锁后进行相应的操作======Thread-3
=============================Thread-3
线程:Thread-4,获取到了锁
======获得锁后进行相应的操作======Thread-4
=============================Thread-4
线程:Thread-30,获取到了锁
======获得锁后进行相应的操作======Thread-30
=============================Thread-30
线程:Thread-8,获取到了锁
======获得锁后进行相应的操作======Thread-8
=============================Thread-8
线程:Thread-7,获取到了锁
======获得锁后进行相应的操作======Thread-7
=============================Thread-7
线程:Thread-31,获取到了锁
======获得锁后进行相应的操作======Thread-31
=============================Thread-31
线程:Thread-16,获取到了锁
======获得锁后进行相应的操作======Thread-16
=============================Thread-16
线程:Thread-5,获取到了锁
======获得锁后进行相应的操作======Thread-5
=============================Thread-5
线程:Thread-25,获取到了锁
======获得锁后进行相应的操作======Thread-25
=============================Thread-25
线程:Thread-12,获取到了锁
线程:Thread-27,获取到了锁
线程:Thread-6,获取到了锁
线程:Thread-17,获取到了锁
线程:Thread-26,获取到了锁
======获得锁后进行相应的操作======Thread-27
=============================Thread-27
======获得锁后进行相应的操作======Thread-26
=============================Thread-26
解锁失败
解锁失败
======获得锁后进行相应的操作======Thread-12
解锁失败
=============================Thread-12
======获得锁后进行相应的操作======Thread-17
=============================Thread-17
解锁失败
======获得锁后进行相应的操作======Thread-6
=============================Thread-6
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。