From 56e25d67e3c9660f639b1e9e58df3a48bd7b81c6 Mon Sep 17 00:00:00 2001 From: houbb <1060732496@qq.com> Date: Mon, 19 Oct 2020 23:38:19 +0800 Subject: [PATCH] [Feature] add for new --- lock-core/pom.xml | 4 + .../houbb/lock/redis/core/LockReadWrite.java | 111 +++++++++++ .../lock/redis/core/LockReadWriteOwner.java | 146 ++++++++++++++ .../lock/redis/core/LockReadWriteRe.java | 182 ++++++++++++++++++ .../houbb/lock/redis/core/LockWaitNotify.java | 12 +- .../lock/redis/core/LockWaitNotifyRe.java | 88 +++++++++ .../lock/test/core/LockWaitNotifyThread.java | 24 +-- .../lock/test/core/LockWaitNotifyThread2.java | 45 +++++ .../test/core/LockWaitNotifyThreadRe.java | 43 +++++ pom.xml | 6 + 10 files changed, 645 insertions(+), 16 deletions(-) create mode 100644 lock-core/src/main/java/com/github/houbb/lock/redis/core/LockReadWrite.java create mode 100644 lock-core/src/main/java/com/github/houbb/lock/redis/core/LockReadWriteOwner.java create mode 100644 lock-core/src/main/java/com/github/houbb/lock/redis/core/LockReadWriteRe.java create mode 100644 lock-core/src/main/java/com/github/houbb/lock/redis/core/LockWaitNotifyRe.java create mode 100644 lock-test/src/main/java/com/github/houbb/lock/test/core/LockWaitNotifyThread2.java create mode 100644 lock-test/src/main/java/com/github/houbb/lock/test/core/LockWaitNotifyThreadRe.java diff --git a/lock-core/pom.xml b/lock-core/pom.xml index 892390d..dd1348a 100644 --- a/lock-core/pom.xml +++ b/lock-core/pom.xml @@ -32,6 +32,10 @@ com.github.houbb heaven + + com.github.houbb + log-integration + diff --git a/lock-core/src/main/java/com/github/houbb/lock/redis/core/LockReadWrite.java b/lock-core/src/main/java/com/github/houbb/lock/redis/core/LockReadWrite.java new file mode 100644 index 0000000..b913dfc --- /dev/null +++ b/lock-core/src/main/java/com/github/houbb/lock/redis/core/LockReadWrite.java @@ -0,0 +1,111 @@ +package com.github.houbb.lock.redis.core; + +import com.github.houbb.log.integration.core.Log; +import com.github.houbb.log.integration.core.LogFactory; + +/** + * 读写锁实现 + * + * @author binbin.hou + * @since 0.0.2 + */ +public class LockReadWrite { + + private static final Log log = LogFactory.getLog(LockReadWrite.class); + + /** + * 读次数统计 + */ + private int readCount = 0; + + /** + * 写次数统计 + */ + private int writeCount = 0; + + /** + * 获取读锁,读锁在写锁不存在的时候才能获取 + * + * @since 0.0.2 + */ + public synchronized void lockRead() throws InterruptedException { + // 写锁存在,需要wait + while (!tryLockRead()) { + wait(); + } + + readCount++; + } + + /** + * 尝试获取读锁 + * + * @return 是否成功 + * @since 0.0.2 + */ + private boolean tryLockRead() { + if (writeCount > 0) { + log.debug("当前有写锁,获取读锁失败"); + return false; + } + + return true; + } + + /** + * 释放读锁 + * + * @since 0.0.2 + */ + public synchronized void unlockRead() { + readCount--; + notifyAll(); + } + + /** + * 获取写锁 + * + * @since 0.0.2 + */ + public synchronized void lockWrite() throws InterruptedException { + // 写锁存在,需要wait + while (!tryLockWrite()) { + wait(); + } + + // 此时已经不存在获取写锁的线程了,因此占坑,防止写锁饥饿 + writeCount++; + } + + /** + * 尝试获取写锁 + * + * @return 是否成功 + * @since 0.0.2 + */ + private boolean tryLockWrite() { + if (writeCount > 0) { + log.debug("当前有其他写锁,获取写锁失败"); + return false; + } + + // 读锁 + if (readCount > 0) { + log.debug("当前有其他读锁,获取写锁失败。"); + return false; + } + + return true; + } + + /** + * 释放写锁 + * + * @since 0.0.2 + */ + public synchronized void unlockWrite() { + writeCount--; + notifyAll(); + } + +} diff --git a/lock-core/src/main/java/com/github/houbb/lock/redis/core/LockReadWriteOwner.java b/lock-core/src/main/java/com/github/houbb/lock/redis/core/LockReadWriteOwner.java new file mode 100644 index 0000000..f3b4980 --- /dev/null +++ b/lock-core/src/main/java/com/github/houbb/lock/redis/core/LockReadWriteOwner.java @@ -0,0 +1,146 @@ +package com.github.houbb.lock.redis.core; + +import com.github.houbb.log.integration.core.Log; +import com.github.houbb.log.integration.core.LogFactory; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; + +/** + * 读写锁实现-保证释放锁时为锁的持有者 + * + * @author binbin.hou + * @since 0.0.2 + */ +public class LockReadWriteOwner { + + private static final Log log = LogFactory.getLog(LockReadWriteOwner.class); + + /** + * 如果使用类似 write 的方式,会导致读锁只能有一个。 + * 调整为使用 HashMap 存放读的信息 + * + * @since 0.0.2 + */ + private final Map readCountMap = new HashMap<>(); + + /** + * volatile 引用,保证线程间的可见性+易变性 + * + * @since 0.0.2 + */ + private final AtomicReference writeOwner = new AtomicReference<>(); + + /** + * 写次数统计 + */ + private int writeCount = 0; + + /** + * 获取读锁,读锁在写锁不存在的时候才能获取 + * + * @since 0.0.2 + */ + public synchronized void lockRead() throws InterruptedException { + // 写锁存在,需要wait + while (!tryLockRead()) { + log.debug("获取读锁失败,进入等待状态。"); + wait(); + } + } + + /** + * 尝试获取读锁 + * + * 读锁之间是不互斥的,这里后续需要优化。 + * + * @return 是否成功 + * @since 0.0.2 + */ + private boolean tryLockRead() { + if (writeCount > 0) { + log.debug("当前有写锁,获取读锁失败"); + return false; + } + + Thread currentThread = Thread.currentThread(); + // 次数暂时固定为1,后面如果实现可重入,这里可以改进。 + this.readCountMap.put(currentThread, 1); + return true; + } + + /** + * 释放读锁 + * + * @since 0.0.2 + */ + public synchronized void unlockRead() { + Thread currentThread = Thread.currentThread(); + Integer readCount = readCountMap.get(currentThread); + + if (readCount == null) { + throw new RuntimeException("当前线程未持有任何读锁,释放锁失败!"); + } else { + log.debug("释放读锁,唤醒所有等待线程。"); + notifyAll(); + } + } + + /** + * 获取写锁 + * + * @since 0.0.2 + */ + public synchronized void lockWrite() throws InterruptedException { + // 写锁存在,需要wait + while (!tryLockWrite()) { + wait(); + } + + // 此时已经不存在获取写锁的线程了,因此占坑,防止写锁饥饿 + writeCount++; + } + + /** + * 尝试获取写锁 + * + * @return 是否成功 + * @since 0.0.2 + */ + private boolean tryLockWrite() { + if (writeCount > 0) { + log.debug("当前有其他写锁,获取写锁失败"); + return false; + } + + // 读锁 + if (!readCountMap.isEmpty()) { + log.debug("当前有其他读锁,获取写锁失败。"); + return false; + } + + Thread currentThread = Thread.currentThread(); + boolean result = writeOwner.compareAndSet(null, currentThread); + log.debug("尝试获取写锁结果:{}", result); + return result; + } + + /** + * 释放写锁 + * + * @since 0.0.2 + */ + public synchronized void unlockWrite() { + boolean toNullResult = writeOwner.compareAndSet(Thread.currentThread(), null); + + if (toNullResult) { + writeCount--; + log.debug("写锁释放,唤醒所有等待线程。"); + notifyAll(); + } else { + throw new RuntimeException("释放写锁失败"); + } + } + +} diff --git a/lock-core/src/main/java/com/github/houbb/lock/redis/core/LockReadWriteRe.java b/lock-core/src/main/java/com/github/houbb/lock/redis/core/LockReadWriteRe.java new file mode 100644 index 0000000..1ce5217 --- /dev/null +++ b/lock-core/src/main/java/com/github/houbb/lock/redis/core/LockReadWriteRe.java @@ -0,0 +1,182 @@ +package com.github.houbb.lock.redis.core; + +import com.github.houbb.log.integration.core.Log; +import com.github.houbb.log.integration.core.LogFactory; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; + +/** + * 读写锁实现-可重入锁 + * + * @author binbin.hou + * @since 0.0.2 + */ +public class LockReadWriteRe { + + private static final Log log = LogFactory.getLog(LockReadWriteRe.class); + + /** + * 如果使用类似 write 的方式,会导致读锁只能有一个。 + * 调整为使用 HashMap 存放读的信息 + * + * @since 0.0.2 + */ + private final Map readCountMap = new HashMap<>(); + + /** + * volatile 引用,保证线程间的可见性+易变性 + * + * @since 0.0.2 + */ + private final AtomicReference writeOwner = new AtomicReference<>(); + + /** + * 写次数统计 + */ + private int writeCount = 0; + + /** + * 获取读锁,读锁在写锁不存在的时候才能获取 + * + * @since 0.0.2 + */ + public synchronized void lockRead() throws InterruptedException { + // 写锁存在,需要wait + while (!tryLockRead()) { + log.debug("获取读锁失败,进入等待状态。"); + wait(); + } + } + + /** + * 尝试获取读锁 + * + * 读锁之间是不互斥的,这里后续需要优化。 + * + * @return 是否成功 + * @since 0.0.2 + */ + private boolean tryLockRead() { + if (writeCount > 0) { + log.debug("当前有写锁,获取读锁失败"); + return false; + } + + Thread currentThread = Thread.currentThread(); + Integer count = readCountMap.get(currentThread); + if(count == null) { + count = 0; + } + count++; + + this.readCountMap.put(currentThread, count); + return true; + } + + /** + * 释放读锁 + * + * @since 0.0.2 + */ + public synchronized void unlockRead() { + Thread currentThread = Thread.currentThread(); + Integer readCount = readCountMap.get(currentThread); + + if (readCount == null) { + throw new RuntimeException("当前线程未持有任何读锁,释放锁失败!"); + } else { + readCount--; + + // 已经是最后一次 + if(readCount == 0) { + readCountMap.remove(currentThread); + } else { + readCountMap.put(currentThread, readCount); + } + + log.debug("释放读锁,唤醒所有等待线程。"); + notifyAll(); + } + } + + /** + * 获取写锁 + * + * @since 0.0.2 + */ + public synchronized void lockWrite() throws InterruptedException { + // 写锁存在,需要wait + while (!tryLockWrite()) { + log.debug("获取写锁失败,进入等待状态。"); + wait(); + } + + // 此时已经不存在获取写锁的线程了,因此占坑,防止写锁饥饿 + writeCount++; + } + + /** + * 尝试获取写锁 + * + * @return 是否成功 + * @since 0.0.2 + */ + private boolean tryLockWrite() { + if (writeCount > 0) { + log.debug("当前有其他写锁,获取写锁失败"); + return false; + } + + // 读锁 + if (!readCountMap.isEmpty()) { + log.debug("当前有其他读锁,获取写锁失败。"); + return false; + } + + Thread currentThread = Thread.currentThread(); + // 多次重入 + if(writeOwner.get() == currentThread) { + log.debug("为当前写线程多次重入,直接返回 true。"); + return true; + } + + boolean result = writeOwner.compareAndSet(null, currentThread); + log.debug("尝试获取写锁结果:{}", result); + return result; + } + + /** + * 释放写锁 + * + * @since 0.0.2 + */ + public synchronized void unlockWrite() { + Thread currentThread = Thread.currentThread(); + // 多次重入释放(当次数多于1时直接返回,否则需要释放 owner 信息) + if(writeCount > 1 && (currentThread == writeOwner.get())) { + log.debug("当前为写锁释放多次重入,直接返回成功。"); + + unlockWriteNotify(); + return; + } + + boolean toNullResult = writeOwner.compareAndSet(currentThread, null); + if (toNullResult) { + unlockWriteNotify(); + } else { + throw new RuntimeException("释放写锁失败"); + } + } + + /** + * 释放写锁并且通知 + */ + private void unlockWriteNotify() { + writeCount--; + log.debug("释放写锁成功,唤醒所有等待线程。"); + notifyAll(); + } + +} diff --git a/lock-core/src/main/java/com/github/houbb/lock/redis/core/LockWaitNotify.java b/lock-core/src/main/java/com/github/houbb/lock/redis/core/LockWaitNotify.java index 739698a..bb5b595 100644 --- a/lock-core/src/main/java/com/github/houbb/lock/redis/core/LockWaitNotify.java +++ b/lock-core/src/main/java/com/github/houbb/lock/redis/core/LockWaitNotify.java @@ -1,6 +1,8 @@ package com.github.houbb.lock.redis.core; import com.github.houbb.lock.redis.exception.LockRuntimeException; +import com.github.houbb.log.integration.core.Log; +import com.github.houbb.log.integration.core.LogFactory; import java.util.concurrent.atomic.AtomicReference; @@ -11,6 +13,8 @@ import java.util.concurrent.atomic.AtomicReference; */ public class LockWaitNotify extends AbstractLock { + private static final Log log = LogFactory.getLog(LockWaitNotify.class); + /** * volatile 引用,保证线程间的可见性+易变性 * @@ -22,6 +26,7 @@ public class LockWaitNotify extends AbstractLock { public synchronized void lock() { while (!tryLock()) { try { + log.debug("等待被唤醒"); wait(); } catch (InterruptedException e) { e.printStackTrace(); @@ -34,7 +39,9 @@ public class LockWaitNotify extends AbstractLock { public boolean tryLock(String key) { Thread current = Thread.currentThread(); // CAS - return owner.compareAndSet(null, current); + boolean result = owner.compareAndSet(null, current); + log.debug("尝试获取锁结果:{}", result); + return result; } @Override @@ -46,7 +53,8 @@ public class LockWaitNotify extends AbstractLock { } // 唤醒等待中的线程 - notifyAll(); + log.debug("唤醒等待的进程"); + notify(); } } diff --git a/lock-core/src/main/java/com/github/houbb/lock/redis/core/LockWaitNotifyRe.java b/lock-core/src/main/java/com/github/houbb/lock/redis/core/LockWaitNotifyRe.java new file mode 100644 index 0000000..7032f76 --- /dev/null +++ b/lock-core/src/main/java/com/github/houbb/lock/redis/core/LockWaitNotifyRe.java @@ -0,0 +1,88 @@ +package com.github.houbb.lock.redis.core; + +import com.github.houbb.lock.redis.exception.LockRuntimeException; +import com.github.houbb.log.integration.core.Log; +import com.github.houbb.log.integration.core.LogFactory; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; + +/** + * 等待通知的锁实现-可重入 + * @author binbin.hou + * @since 0.0.2 + */ +public class LockWaitNotifyRe extends AbstractLock { + + private static final Log log = LogFactory.getLog(LockWaitNotifyRe.class); + + /** + * volatile 引用,保证线程间的可见性+易变性 + * + * @since 0.0.2 + */ + private AtomicReference owner =new AtomicReference<>(); + + /** + * 次数统计 + * @since 0.0.2 + */ + private AtomicInteger count = new AtomicInteger(0); + + @Override + public synchronized void lock() { + while (!tryLock()) { + try { + log.debug("等待被唤醒"); + wait(); + } catch (InterruptedException e) { + e.printStackTrace(); + // 是否可以被打断 + } + } + } + + @Override + public boolean tryLock(String key) { + Thread current = Thread.currentThread(); + + //可重入实现 + if(current == owner.get()) { + count.incrementAndGet(); + log.debug("当前线程已经拥有锁,直接返回 true"); + return true; + } + + // CAS + boolean result = owner.compareAndSet(null, current); + log.debug("尝试获取锁结果:{}", result); + return result; + } + + @Override + public synchronized void unlock(String key) { + Thread current = Thread.currentThread(); + + // 可重入实现 + if(owner.get() == current && count.get() != 0) { + count.decrementAndGet(); + notifyAndLog(); + return; + } + + boolean result = owner.compareAndSet(current, null); + if(!result) { + throw new LockRuntimeException("解锁失败"); + } + + notifyAndLog(); + } + + private void notifyAndLog() { + // 唤醒等待中的线程 + log.debug("唤醒等待的进程"); + notify(); + } + +} diff --git a/lock-test/src/main/java/com/github/houbb/lock/test/core/LockWaitNotifyThread.java b/lock-test/src/main/java/com/github/houbb/lock/test/core/LockWaitNotifyThread.java index f2da746..ae64102 100644 --- a/lock-test/src/main/java/com/github/houbb/lock/test/core/LockWaitNotifyThread.java +++ b/lock-test/src/main/java/com/github/houbb/lock/test/core/LockWaitNotifyThread.java @@ -1,7 +1,10 @@ package com.github.houbb.lock.test.core; +import com.github.houbb.heaven.util.util.DateUtil; import com.github.houbb.lock.api.core.ILock; import com.github.houbb.lock.redis.core.LockWaitNotify; +import com.github.houbb.log.integration.core.Log; +import com.github.houbb.log.integration.core.LogFactory; import java.util.concurrent.TimeUnit; @@ -11,26 +14,19 @@ import java.util.concurrent.TimeUnit; */ public class LockWaitNotifyThread implements Runnable { + private static final Log log = LogFactory.getLog(LockWaitNotifyThread.class); + private final ILock lock = new LockWaitNotify(); @Override public void run() { - System.out.println("first-lock: " + Thread.currentThread().getId()); + log.debug("first lock"); + lock.lock(); - -// System.out.println("second-lock: " + Thread.currentThread().getId()); -// lock.lock(); -// lock.unlock(); -// System.out.println("second-unlock: " + Thread.currentThread().getId()); - - - try { - TimeUnit.SECONDS.sleep(5); - } catch (InterruptedException e) { - e.printStackTrace(); - } + log.info("执行业务逻辑。"); + DateUtil.sleep(TimeUnit.SECONDS, 5); lock.unlock(); - System.out.println("first-unlock: " + Thread.currentThread().getId()); + log.debug("first unlock"); } public static void main(String[] args) { diff --git a/lock-test/src/main/java/com/github/houbb/lock/test/core/LockWaitNotifyThread2.java b/lock-test/src/main/java/com/github/houbb/lock/test/core/LockWaitNotifyThread2.java new file mode 100644 index 0000000..2228c10 --- /dev/null +++ b/lock-test/src/main/java/com/github/houbb/lock/test/core/LockWaitNotifyThread2.java @@ -0,0 +1,45 @@ +package com.github.houbb.lock.test.core; + +import com.github.houbb.heaven.util.util.DateUtil; +import com.github.houbb.lock.api.core.ILock; +import com.github.houbb.lock.redis.core.LockWaitNotify; +import com.github.houbb.log.integration.core.Log; +import com.github.houbb.log.integration.core.LogFactory; + +import java.util.concurrent.TimeUnit; + +/** + * @author binbin.hou + * @since 1.0.0 + */ +public class LockWaitNotifyThread2 implements Runnable { + + private static final Log log = LogFactory.getLog(LockWaitNotifyThread2.class); + + private final ILock lock = new LockWaitNotify(); + + @Override + public void run() { + log.debug("first lock"); + + lock.lock(); + + log.debug("second lock"); + lock.lock(); + log.info("执行业务逻辑。"); + DateUtil.sleep(TimeUnit.SECONDS, 5); + log.debug("second unlock"); + lock.unlock(); + + lock.unlock(); + log.debug("first unlock"); + } + + public static void main(String[] args) { + final Runnable runnable = new LockWaitNotifyThread2(); + new Thread(runnable).start(); + new Thread(runnable).start(); + new Thread(runnable).start(); + } + +} diff --git a/lock-test/src/main/java/com/github/houbb/lock/test/core/LockWaitNotifyThreadRe.java b/lock-test/src/main/java/com/github/houbb/lock/test/core/LockWaitNotifyThreadRe.java new file mode 100644 index 0000000..ed98224 --- /dev/null +++ b/lock-test/src/main/java/com/github/houbb/lock/test/core/LockWaitNotifyThreadRe.java @@ -0,0 +1,43 @@ +package com.github.houbb.lock.test.core; + +import com.github.houbb.heaven.util.util.DateUtil; +import com.github.houbb.lock.api.core.ILock; +import com.github.houbb.lock.redis.core.LockWaitNotify; +import com.github.houbb.lock.redis.core.LockWaitNotifyRe; +import com.github.houbb.log.integration.core.Log; +import com.github.houbb.log.integration.core.LogFactory; + +import java.util.concurrent.TimeUnit; + +/** + * @author binbin.hou + * @since 1.0.0 + */ +public class LockWaitNotifyThreadRe implements Runnable { + + private static final Log log = LogFactory.getLog(LockWaitNotifyThreadRe.class); + + private final ILock lock = new LockWaitNotifyRe(); + + @Override + public void run() { + log.debug("first lock"); + lock.lock(); + + log.debug("second lock"); + lock.lock(); + log.debug("second unlock"); + lock.unlock(); + + log.debug("first unlock"); + lock.unlock(); + } + + public static void main(String[] args) { + final Runnable runnable = new LockWaitNotifyThreadRe(); + new Thread(runnable).start(); + new Thread(runnable).start(); + new Thread(runnable).start(); + } + +} diff --git a/pom.xml b/pom.xml index d9d15da..d6b073a 100644 --- a/pom.xml +++ b/pom.xml @@ -36,6 +36,7 @@ 0.1.114 0.0.6 0.0.1 + 1.1.8 4.12 @@ -72,6 +73,11 @@ wait ${wait.version} + + com.github.houbb + log-integration + ${log-integration.version} +