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(); } }