最直观的zookeeper分布式锁,Lock和线程池的简单使用

一直对zookeeper的分布式锁没有什么概念,今天研究了一下,在此发表下心得.此处只说可重入锁.别的网上讲解很多

先说一下我的需求:要在多线程的情况下,同时对一个文件读写,并且保证读写的原子性和一致性(本人基本概念表达的不是很准确的地方见谅),我喜欢用代码说话.

先看文件D:\\a.txt,此处用a.txt当做一个资源.

此时只有第一行有个*,以后每一行都要多一个*,并且要在多线程的情况下进行读写

我的代码如下,代码结构来自https://blog.csdn.net/haoyuyang/article/details/53469269

pom.xml如下:

		<dependency>
			<groupId>org.apache.curator</groupId>
			<artifactId>curator-framework</artifactId>
			<version>2.9.0</version>
		</dependency>
		<dependency>
			<groupId>org.apache.curator</groupId>
			<artifactId>curator-recipes</artifactId>
			<version>2.9.0</version>
		</dependency>
		<dependency>
			<groupId>org.apache.curator</groupId>
			<artifactId>curator-client</artifactId>
			<version>2.9.0</version>
		</dependency>

一下代码是包含了zookeeper的API,一开始的几个测试代码是没有用的zookeeper的,大家可以先把CuratorFramework相关的删掉,从头来,很简单,在此就不繁琐的列代码了,大家在下面代码上自由发挥下

package test.zklock;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

public class FakeLimitedResource {

	public void use() throws InterruptedException{

		FileReader read = null;
		BufferedReader bufferedReader = null;
		FileWriter fileWriter = null;
		try {
			read = new FileReader(new File("D:\\a.txt"));
			
			bufferedReader =new BufferedReader(read);
			String str = null;
			List<String> strList = new ArrayList<String>();
			while((str=bufferedReader.readLine()) != null){
				strList.add(str);
			}
			
			String outStr = " \n" + strList.get(strList.size()-1) + "*";
			fileWriter =new FileWriter(new File("D:\\a.txt"), true);
			fileWriter.write(outStr);
			fileWriter.flush();
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
			if(bufferedReader != null){
				try {
					bufferedReader.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			
			if(fileWriter != null){
				try {
					fileWriter.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		
	}
}
package test.zklock;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.locks.InterProcessLock;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;

import com.sun.xml.internal.txw2.IllegalAnnotationException;

public class ExampleClientThatLock {
	private final InterProcessMutex lock;
	private final String clientName;
	private final FakeLimitedResource resource;
	private Lock lk = new ReentrantLock();
	public ExampleClientThatLock(CuratorFramework curator,String clientName, FakeLimitedResource resource,String path){
		this.lock = new InterProcessMutex(curator, path);
		this.clientName = clientName;
		this.resource = resource;
	}
	
	public void doWork(long time,TimeUnit timeUnit) throws Exception{
//		if(!lock.acquire(time, timeUnit)){
//			throw new IllegalStateException("clientName: " + this.clientName + "cloudn't acquire the lock");
//		}
        try {
		    //1-使用ReentrantLock
		    //this.lk.lock();
		    System.out.println(this.clientName + "get lock");
		
			resource.use();
		} finally {
			System.out.println(this.clientName + "releasing the lock");
			//this.lock.release();
			//1-释放ReentrantLock
			//this.lk.unlock();
		}
				
	}
}

 

package test.zklock;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

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 InterProcessMutexExample {
	
	private static final int QTY=5;
	private static final int REPETITIONS = QTY*2;
	private final static String CONNECT_ADDRESS = "172.25.90.10:2181,172.25.90.11:2181,172.25.90.12:2181";
	private final static String PATH = "/test02/a";
	

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		try {
			final FakeLimitedResource resource = new FakeLimitedResource();
			ExecutorService executor = Executors.newFixedThreadPool(QTY);
			
			for (int i = 0; i < QTY; i++) {
				final int index = i;
				Callable<Void> task = new Callable<Void>() {
					
					@Override
					public Void call() throws Exception {
						CuratorFramework curator = CuratorFrameworkFactory.newClient(CONNECT_ADDRESS, new ExponentialBackoffRetry(1000, 10));
						try {
							curator.start();
							
							final ExampleClientThatLock client = new ExampleClientThatLock(curator, "client_" + index, resource, PATH);
							for (int j = 0; j < REPETITIONS; j++) {
								client.doWork(10, TimeUnit.SECONDS);
							}
						} catch (Exception e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						} finally {
							CloseableUtils.closeQuietly(curator);
						}
						return null;
					}
				};
				executor.submit(task);			
			}
			executor.shutdown();
			executor.awaitTermination(500, TimeUnit.SECONDS);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

}

建议大家把代码跑一下,重点代码在此处:doWork 方法.一下没有用到zk的时候可以忽略zk相关的代码,大家试着

第一步: 不加锁的情况下,打印结果如下:具体

a.txt结果如下

加上synchronized结果也是如上面(a.txt可能有点变化但是想要的结果没出来)

加上 ReentrantLock如下

依然没有效果.为什么lock没有起作用呢?

可能InterProcessMutexExample代码有点难懂,咱们换个简单的

package test.zklock;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;

public class LockTest {
	private static final int QTY=5;
	private static final int REPETITIONS = QTY*2;
	private final static String CONNECT_ADDRESS = "172.25.90.10:2181,172.25.90.11:2181,172.25.90.12:2181";
	private final static String PATH = "/test02/a";
	
	public static void main(String[] args) {
				
		Thread t1 = new Thread(new Runnable() {
			
			@Override
			public void run() {
				CuratorFramework curator = CuratorFrameworkFactory.newClient(CONNECT_ADDRESS, new ExponentialBackoffRetry(1000, 10));
				final FakeLimitedResource resource = new FakeLimitedResource();
				final ExampleClientThatLock client = new ExampleClientThatLock(curator, "client_" + 1, resource, PATH);
				for (int j = 0; j < REPETITIONS; j++) {
					try {
						client.doWork(10, TimeUnit.SECONDS);
					} catch (Exception e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				
			}
		});
		Thread t2 = new Thread(new Runnable() {
			
			@Override
			public void run() {
				CuratorFramework curator = CuratorFrameworkFactory.newClient(CONNECT_ADDRESS, new ExponentialBackoffRetry(1000, 10));
				final FakeLimitedResource resource = new FakeLimitedResource();
				final ExampleClientThatLock client = new ExampleClientThatLock(curator, "client_" + 2, resource, PATH);
				for (int j = 0; j < REPETITIONS; j++) {
					try {
						client.doWork(10, TimeUnit.SECONDS);
					} catch (Exception e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				
			}
		});
		
		t1.start();
		t2.start();
	}
}

 

此处不过多解释为什么它俩的作用一样,如有不懂请学习下Future模式和jdk自带的Future

结果:

此处是20行,最早的是50行,这个相信大家都能懂,如果这个不懂,需要补习基础了呦

为什么还是不一样呢?

来看下面这个代码:

package test.zklock;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;

public class LockTest {
	private static final int QTY=5;
	private static final int REPETITIONS = QTY*2;
	private final static String CONNECT_ADDRESS = "172.25.90.10:2181,172.25.90.11:2181,172.25.90.12:2181";
	private final static String PATH = "/test02/a";
	
	public static void main(String[] args) {
		CuratorFramework curator = CuratorFrameworkFactory.newClient(CONNECT_ADDRESS, new ExponentialBackoffRetry(1000, 10));
		final FakeLimitedResource resource = new FakeLimitedResource();
		final ExampleClientThatLock client = new ExampleClientThatLock(curator, "client_" + 1, resource, PATH);
		
		Thread t1 = new Thread(new Runnable() {
			
			@Override
			public void run() {

				for (int j = 0; j < REPETITIONS; j++) {
					try {
						client.doWork(10, TimeUnit.SECONDS);
					} catch (Exception e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				
			}
		});
		Thread t2 = new Thread(new Runnable() {
			
			@Override
			public void run() {
				for (int j = 0; j < REPETITIONS; j++) {
					try {
						client.doWork(10, TimeUnit.SECONDS);
					} catch (Exception e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				
			}
		});
		
		t1.start();
		t2.start();
	}
}

 

结果:

为什么这次可以了呢?

大家注意看下,两处的代码不一样的地方在,线程用的对象上面,不可以的线程用的对象都是各自线程自有的对象,而最后一次用的是一个对象.

此时回想一下需求,大家是为了保证a.txt文件的线程安全,有人会说,我用最后一种方式不就可以了,此处回到分布式的重点上来看,如果是2个不同应用去读写a.txt文件呢?

此时我们用下下面的代码:此处用到了zookeeper的分布式锁InterProcessMutex

大家可以把项目再复制一份,2个同时运行试试

此处设置大一点可以看到运行结果

太大了,此处不截图了,大家试试

关于InterProcessMutex的原理,大家可以看下https://blog.csdn.net/qiangcuo6087/article/details/79067136

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值