对于单机环境
- 一个JVM对应多个线程,使用synchronized,Reentrantlock。
- 代码实现
- 方法加lock锁
public class OrderZookService{ private static int number = 0; private ZkLock zkLock = new ZkDistributedLock(); public String getOrdNumber(){ String ret = null; zkLock.zkLock(); try{ ret = "\t 生成订单号:" + number; number ++; }catch (Exception e){ // e.printStackTrace(); }finally { zkLock.zkUnlock(); } return ret; } }
- test
public static void main(String[] args) { //单机模式 OrderService orderService = new OrderService(); for (int i = 0; i < 20; i++) { new Thread(()->{ orderService.getOrdNumber(); }, "线程" + i).start(); } }
- 当改成多太服务机器访问,订单重复
public static void main(String[] args) { //单机模式 OrderService orderService = new OrderService(); for (int i = 0; i < 20; i++) { new Thread(()->{ //每次new一次来模拟多台机器 结果订单编号重复 System.out.println( new OrderService().getOrdNumber()); }, "线程" + i).start(); } }
- 方法加lock锁
对于多台服务器,分布式环境
- 多个JVM对应多个线程,上面的锁不好使。
问题:如何保证创建订单不重复。
- 2000访问一下,使用数据库乐观锁,企业不使用。
- redis锁
- zookeeper锁
zookeeper锁实现机制
- 创建锁
- 释放锁
zookeeper锁实现代码
- 创建接口
/** * 分布式锁 */ public interface ZkLock { public void zkLock(); public void zkUnlock(); }
- 创建抽象模板
/** * 为什么使用zookeeper加锁? * 因为zookeeper会一次只能创建临时节点,当关闭临时节点就自动删除。 * 该类升级为模板类 */ public abstract class ZkAbstractTemplateLock implements ZkLock { public static final String zkServers = "192.168.3.75:2181"; public static final int timeout = 45*1000; //zookeeper临时节点 protected String path = "/zkLock0401"; protected CountDownLatch countDownLatch = null; ZkClient zkClient = new ZkClient(zkServers, timeout); /** * 分布式加锁 */ @Override public void zkLock() { //如果没有创建节点,则创建节点,创建成功则占用锁成功,否则占用失败则等待,进行递归循环占用,直到占用成功! if(tryzkLock()){ System.out.println(Thread.currentThread().getName() + "\t 占用锁成功"); }else { waitZkLock(); zkLock(); } } public abstract void waitZkLock(); public abstract boolean tryzkLock(); /** * 分布式解锁 */ @Override public void zkUnlock() { if(zkClient != null){ zkClient.close(); } System.out.println(Thread.currentThread().getName() + "\t 释放锁成功"); } }
- 将抽象的具体实现放到子类
package com.zookeeper.demo.zookeeper; import org.I0Itec.zkclient.IZkDataListener; import java.util.concurrent.CountDownLatch; /** * 集成父类,需要的东西都在父类,可以直接使用。 */ public class ZkDistributedLock extends ZkAbstractTemplateLock { /** * 判断是否创建成功 * @return */ @Override public boolean tryzkLock() { try{ //创建成功 zkClient.createEphemeral(path); return true; }catch (Exception e){ //创建失败 return false; } } /** * 等待 */ @Override public void waitZkLock() { //监听 IZkDataListener iZkDataListener = new IZkDataListener() { @Override public void handleDataChange(String s, Object o) throws Exception { } @Override public void handleDataDeleted(String s) throws Exception { if(countDownLatch != null){ countDownLatch.countDown(); } } }; zkClient.subscribeDataChanges(path, iZkDataListener); if(zkClient.exists(path)){ //只能等着,不能往下走 countDownLatch = new CountDownLatch(1); try{ //等待,相当于睡眠,使用sleep太low countDownLatch.await(); }catch (Exception e){ // e.printStackTrace(); } } zkClient.unsubscribeDataChanges(path, iZkDataListener); } }
- 编写service
public class OrderService { private static int number = 0; private Lock lock = new ReentrantLock(); public String getOrdNumber(){ lock.lock(); String ret = null; try{ ret = "\t 生成订单号:" + number; number ++; }catch (Exception e){ e.printStackTrace(); }finally { lock.unlock(); } return ret; } }
- test
public static void main(String[] args) { //多台服务 for (int i = 0; i < 10; i++) { new Thread(()->{ OrderZookService orderZookService = new OrderZookService(); System.out.println(orderZookService.getOrdNumber()); }, "线程" + i).start(); } }
- 结果:订单编号没有重复的