InterProcessMutex:全局可重入锁,客户端都可以请求锁,并且同一个客户端在拥有锁的同时,可以多次获取,不会被阻塞。客户端在拥有锁的同时,可以多次获取,不会被阻塞。
package DistributeLock;
import java.util.concurrent.TimeUnit;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
/**
* 这个类负责请求锁,使用资源,释放锁这个完整的访问流程
* */
public class ExampleClientThatLocks
{
private final InterProcessMutex lock;//全局可重入锁
private final FakeLimitedResource resource;//共享资源
private final String clientName;
public ExampleClientThatLocks(CuratorFramework client,String lockPath, FakeLimitedResource resource, String clientName)
{
this.lock = new InterProcessMutex(client, lockPath);
this.resource = resource;
this.clientName = clientName;
}
public void doWork(long time,TimeUnit unit) throws Exception{
//尝试获取锁,如果无法获取锁,抛出异常
if(!lock.acquire(time, unit)){
throw new IllegalStateException(clientName + "不能获取锁");
}
System.out.println(clientName +"获得了锁");
if(!lock.acquire(time, unit)){
throw new IllegalStateException(clientName + "不能再次获取锁");
}
System.out.println(clientName +"再次获得了锁(重入功能)");
try{
resource.use();
}finally{
System.out.println(clientName + "释放了锁");
lock.release();
lock.release();
}
}
}
package DistributeLock;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* 模拟共享资源,这个资源期望只能单线程访问,否则会有并发问题(走到抛异常的逻辑)
* <br>
*/
public class FakeLimitedResource {
private final AtomicBoolean inUse = new AtomicBoolean(false);
public void use() throws InterruptedException {
/**
* inUse.compareAndSet(false, true)表示:先检测当前值是否为false,如果为false,就更新为true
* 如果多个线程执行这个方法,在无锁的情况下,由于下面睡了一段时间,所以有些线程会走到抛异常的地方
*/
if (!inUse.compareAndSet(false, true)) {
throw new IllegalStateException("Needs to be used by one client at a time");
}
try {
Thread.sleep(5000);
} finally {
inUse.set(false);
}
}
public void read(){
System.out.println(inUse.get());
}
}
package DistributeLock;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.curator.test.TestingServer;
import org.apache.curator.utils.CloseableUtils;
/**
*可重入锁InterProcessMutex例子
*
*/
public class InterProcessMutexExample
{
private static final String PATH = "/examples/locks";
public static void main(String[] args) throws Exception
{
//共享资源
final FakeLimitedResource resource = new FakeLimitedResource();
//有50个线程的线程池
ExecutorService service = Executors.newFixedThreadPool(10);
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 5);
//10个线程,模拟10个客户端
for(int i=0;i<10;++i){
final int index = i;
Callable<Void> task = new Callable<Void>()
{
public Void call() throws Exception
{
//2.通过工厂建立连接
CuratorFramework client = CuratorFrameworkFactory.builder().connectString("127.0.0.1:2181")
.sessionTimeoutMs(5000)
.retryPolicy(retryPolicy)
.build();
try
{
client.start();
ExampleClientThatLocks example = new ExampleClientThatLocks(client, PATH, resource,"client" + index);
//每个客户端重复5次任务
for(int j=0;j<5;++j){
example.doWork(10, TimeUnit.SECONDS);
}
}
catch (Exception e)
{
e.printStackTrace();
}finally{
CloseableUtils.closeQuietly(client);//关闭该客户端
}
return null;
}
};
service.submit(task);
}
service.shutdown();
}
}
代码也很简单,生成10个client, 每个client重复执行5次 请求锁–访问资源–释放锁的过程。每个client都在独立的线程中。结果可以看到,锁是随机的被每个客户端实例排他性的使用。既然是可重用的,你可以在一个线程中多次调用acquire,在线程拥有锁时它总是返回true。
你不应该在多个线程中用同一个InterProcessMutex,你可以在每个线程中都生成一个InterProcessMutex实例,它们的path都一样,这样它们可以共享同一个锁。
InterProcessSemaphoreMutex:不可重入锁
这个锁和上面的相比,就是少了Reentrant的功能,也就意味着它不能在同一个线程中重入。 这个类是InterProcessSemaphoreMutex。使用方法和上面的类类似。
我们将上面的例子修改一下,测试一下它的重入,修改ExampleClientThatLocks.doWork,连续两次acquire。现在第二次acquire进不去了。
package curatorExample.DistributeLock;
import java.util.concurrent.TimeUnit;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.curator.framework.recipes.locks.InterProcessSemaphoreMutex;
/**
* 测试不可重入锁
* 这个类负责请求锁,使用资源,释放锁这个完整的访问流程
* */
public class ExampleClientThatLocks2
{
private final InterProcessSemaphoreMutex lock;//全局可重入锁
private final FakeLimitedResource resource;//共享资源
private final String clientName;
public ExampleClientThatLocks2(CuratorFramework client,String lockPath, FakeLimitedResource resource, String clientName)
{
this.lock = new InterProcessSemaphoreMutex(client, lockPath);//不可重入的锁
this.resource = resource;
this.clientName = clientName;
}
public void doWork(long time,TimeUnit unit) throws Exception{
//尝试获取锁,如果无法获取锁,抛出异常
if(!lock.acquire(time, unit)){
throw new IllegalStateException(clientName + "不能获取锁");
}
System.out.println(clientName +"获得了锁");
//现在这里不能重入了
if(!lock.acquire(time, unit)){
throw new IllegalStateException(clientName + "不能再次获取锁");
}
System.out.println(clientName +"再次获得了锁(重入功能)");
try{
resource.use();
}finally{
System.out.println(clientName + "释放了锁");
lock.release();
lock.release();
}
}
}
InterProcessReadWriteLock:可重入读写锁
类似JDK的ReentrantReadWriteLock. 一个读写锁管理一对相关的锁。 一个负责读操作,另外一个负责写操作。读操作在写锁没被使用时可同时由多个进程使用,而写锁使用时不允许读 (阻塞)。 此锁是可重入的。一个拥有写锁的线程可重入读锁,但是读锁却不能进入写锁。 这也意味着写锁可以降级成读锁, 比如请求写锁 —>读锁 —->释放写锁。从读锁升级成写锁是不成的。
主要由两个类实现:
InterProcessReadWriteLock
InterProcessLock
使用时首先创建一个InterProcessReadWriteLock实例,然后再根据你的需求得到读锁或者写锁, 读写锁的类型是InterProcessLock。
public InterProcessLock readLock()
public InterProcessLock writeLock()
package curatorExample.DistributeLock;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.locks.InterProcessLock;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.curator.framework.recipes.locks.InterProcessReadWriteLock;
public class ExampleClientReadWriteLocks
{
private final InterProcessReadWriteLock lock;
private final InterProcessLock readLock;
private final InterProcessLock writeLock;
private final FakeLimitedResource resource;
private final String clientName;
public ExampleClientReadWriteLocks(CuratorFramework client, String lockPath, FakeLimitedResource resource, String clientName) {
this.resource = resource;
this.clientName = clientName;
lock = new InterProcessReadWriteLock(client, lockPath);
readLock = lock.readLock();
writeLock = lock.writeLock();
}
public void doWork(long time,TimeUnit unit) throws Exception{
Random rand = new Random();
int random_data = rand.nextInt();
try{
if(random_data %10 == 0){
//尝试获取写锁,
if(!writeLock.acquire(time, unit)){
throw new IllegalStateException(clientName + "不能获得写锁");
}
System.out.println(clientName + " 获得了写锁");
resource.use();
}else{
//尝试获取读锁
if(!readLock.acquire(time, unit)){
throw new IllegalStateException(clientName + "不能获得读锁");
}
System.out.println(clientName + " 获得了读锁");
resource.read();
}
}finally{
System.out.println(clientName + " 释放锁");
if(writeLock.isAcquiredInThisProcess())
writeLock.release();
if(readLock.isAcquiredInThisProcess())
readLock.release();
}
}
}
package curatorExample.DistributeLock;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.curator.utils.CloseableUtils;
public class InterProcessReadWriteLockExample
{
private static final String PATH = "/examples/locks";
public static void main(String[] args) throws Exception
{
//共享资源
final FakeLimitedResource resource = new FakeLimitedResource();
//有10个线程的线程池
ExecutorService service = Executors.newFixedThreadPool(10);
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 5);
//10个线程,模拟10个客户端
for(int i=0;i<10;++i){
final int index = i;
Callable<Void> task = new Callable<Void>()
{
public Void call() throws Exception
{
//2.通过工厂建立连接
CuratorFramework client = CuratorFrameworkFactory.builder().connectString("192.168.10.110:2181,192.168.10.111:2181,192.168.10.112:2181")
.sessionTimeoutMs(5000)
.retryPolicy(retryPolicy)
.build();
try
{
client.start();
ExampleClientReadWriteLocks example = new ExampleClientReadWriteLocks(client, PATH, resource,"client" + index);
//每个客户端重复5次任务
for(int j=0;j<5;++j){
example.doWork(10, TimeUnit.SECONDS);
}
}
catch (Exception e)
{
e.printStackTrace();
}finally{
CloseableUtils.closeQuietly(client);//关闭该客户端
}
return null;
}
};
service.submit(task);
}
service.shutdown();
}
}