Key锁ITeye - 众发娱乐

Key锁ITeye

2019-01-12 00:58:07 | 作者: 晨潍 | 标签: 转账,加锁,账户 | 浏览: 1074

java中的几种锁:synchronized,ReentrantLock,ReentrantReadWriteLock已基本能够满意编程需求,但其粒度都太大,同一时间只要一个线程能进入同步块,这关于某些高并发的场景并不适用。本文完成了一个根据KEY(主键)的互斥锁,具有更细的粒度,在缓存或其他根据KEY的场景中有很大的用途。下面将解说这个锁的规划和完成

(关于这个锁的评论贴:KeyLock评论贴-CSDN)

 

想象这么一个场景:转账

 

 private int[] accounts; // 账户数组,其索引为账户ID,内容为金额
 public boolean transfer(int from, int to, int money) {
 if (accounts[from] money)
 return false;
 accounts[from] -= money;
 accounts[to] += money;
 return true;
 }

 从from中转出金额到to中。或许一起会有很多个线程一起调用这个转账办法,为确保原子性,确保金额不会犯错,有必要为这个办法加个锁,避免对同享变量accounts的并发修正。

 

 

 

加锁后的代码如下:

 private int[] accounts; // 账户数组,其索引为账户ID,内容为金额
 private Lock lock = new ReentrantLock();
 public boolean transfer(int from, int to, int money) {
 lock.lock();
 try {
 if (accounts[from] money)
 return false;
 accounts[from] -= money;
 accounts[to] += money;
 return true;
 } finally {
 lock.unlock();
 }

 好了,加锁后这个代码就能确保金额不犯错了。但问题又呈现了,一次只能履行一个转账进程!意思便是A给B转账的时分,C要给D转账也得等A给B转完了才干开端转。这就有点扯蛋了,就像只要一个货台,一切人有必要排队等前面的处理完了才干到自己,功率太低。

 

 

处理这种状况有一个计划:A给B转账的时分只确定A和B的账户,使其转账期间不能再有其他针对A和B账户的操作,但其他账户的操作能够并行发作。类似于如下场景:

 public boolean transfer(int from, int to, int money) {
 lock.lock(from, to);
 try {
 if (accounts[from] money)
 return false;
 accounts[from] -= money;
 accounts[to] += money;
 return true;
 } finally {
 lock.unlock(from, to);
 }

 但很显然,JAVA并没有为咱们供给这样的锁(也有或许是我没找到。。。)

 

 

所以,就在这样的需求下我花了整一天来完成了这个锁——KeyLock(代码量很短,但多线程的东西真的很让人头疼)

不同于synchronized等锁,KeyLock是对所需处理的数据的KEY(主键)进行加锁,只要是对不同key操作,其就能够并行处理,大大提高了线程的并行度(最终有几个锁的比照测验)

总结下便是:对相同KEY操作的线程互斥,对不同KEY操作的线程能够并行

 

KeyLock有如下几个特性:

    1、细粒度,高并行性
    2、可重入
    3、公正锁
    4、加锁开支比ReentrantLock大,适用于处理耗时长、key规模大的场景

 

KeyLock代码如下(注释很少,由于我也不知道该怎样写清楚,能看懂就看,懒得看的直接用就行):

public class KeyLock K {
 // 保存一切确定的KEY及其信号量
 private final ConcurrentMap K, Semaphore map = new ConcurrentHashMap K, Semaphore 
 // 保存每个线程确定的KEY及其确定计数
 private final ThreadLocal Map K, LockInfo local = new ThreadLocal Map K, LockInfo () {
 @Override
 protected Map K, LockInfo initialValue() {
 return new HashMap K, LockInfo 
 * 确定key,其他等候此key的线程将进入等候,直到调用{@link #unlock(K)}
 * 运用hashcode和equals来判别key是否相同,因而key有必要完成{@link #hashCode()}和
 * {@link #equals(Object)}办法
 * @param key
 public void lock(K key) {
 if (key == null)
 return;
 LockInfo info = local.get().get(key);
 if (info == null) {
 Semaphore current = new Semaphore(1);
 current.acquireUninterruptibly();
 Semaphore previous = map.put(key, current);
 if (previous != null)
 previous.acquireUninterruptibly();
 local.get().put(key, new LockInfo(current));
 } else {
 info.lockCount++;
 * 开释key,唤醒其他等候此key的线程
 * @param key
 public void unlock(K key) {
 if (key == null)
 return;
 LockInfo info = local.get().get(key);
 if (info != null --info.lockCount == 0) {
 info.current.release();
 map.remove(key, info.current);
 local.get().remove(key);
 * 确定多个key
 * 主张在调用此办法前先对keys进行排序,运用相同的确定次序,避免死锁发作
 * @param keys
 public void lock(K[] keys) {
 if (keys == null)
 return;
 for (K key : keys) {
 lock(key);
 * 开释多个key
 * @param keys
 public void unlock(K[] keys) {
 if (keys == null)
 return;
 for (K key : keys) {
 unlock(key);
 private static class LockInfo {
 private final Semaphore current;
 private int lockCount;
 private LockInfo(Semaphore current) {
 this.current = current;
 this.lockCount = 1;
}

 KeyLock运用示例:

 

 private int[] accounts;
 private KeyLock Integer lock = new KeyLock Integer 
 public boolean transfer(int from, int to, int money) {
 Integer[] keys = new Integer[] {from, to};
 Arrays.sort(keys); //对多个key进行排序,确保确定次序避免死锁
 lock.lock(keys);
 try {
 //处理不同的from和to的线程都可进入此同步块
 if (accounts[from] money)
 return false;
 accounts[from] -= money;
 accounts[to] += money;
 return true;
 } finally {
 lock.unlock(keys);
 }

 好,东西有了,接下来便是测验了,为了测出并行度,我把转账进程延长了,加了个sleep(2),使每个转账进程至少要花2毫秒(这仅仅个demo,实在环境下对数据库操作也很费时)。

 

 

测验代码如下:

//场景:多线程并发转账
public class Test {
 private final int[] account; // 账户数组,其索引为账户ID,内容为金额
 public Test(int count, int money) {
 account = new int[count];
 Arrays.fill(account, money);
 boolean transfer(int from, int to, int money) {
 if (account[from] money)
 return false;
 account[from] -= money;
 try {
 Thread.sleep(2);
 } catch (Exception e) {
 account[to] += money;
 return true;
 int getAmount() {
 int result = 0;
 for (int m : account)
 result += m;
 return result;
 public static void main(String[] args) throws Exception {
 int count = 100; //账户个数
 int money = 10000; //账户初始金额
 int threadNum = 8; //转账线程数
 int number = 10000; //转账次数
 int maxMoney = 1000; //随机转账最大金额
 Test test = new Test(count, money);
 //不加锁
// Runner runner = test.new NonLockRunner(maxMoney, number);
 //加synchronized锁
// Runner runner = test.new SynchronizedRunner(maxMoney, number);
 //加ReentrantLock锁
// Runner runner = test.new ReentrantLockRunner(maxMoney, number);
 //加KeyLock锁
 Runner runner = test.new KeyLockRunner(maxMoney, number);
 Thread[] threads = new Thread[threadNum];
 for (int i = 0; i threadNum; i++)
 threads[i] = new Thread(runner, "thread-" + i);
 long begin = System.currentTimeMillis();
 for (Thread t : threads)
 t.start();
 for (Thread t : threads)
 t.join();
 long time = System.currentTimeMillis() - begin;
 System.out.println("类型:" + runner.getClass().getSimpleName());
 System.out.printf("耗时:%dms\n", time);
 System.out.printf("初始总金额:%d\n", count * money);
 System.out.printf("停止总金额:%d\n", test.getAmount());
 // 转账使命
 abstract class Runner implements Runnable {
 final int maxMoney;
 final int number;
 private final Random random = new Random();
 private final AtomicInteger count = new AtomicInteger();
 Runner(int maxMoney, int number) {
 this.maxMoney = maxMoney;
 this.number = number;
 @Override
 public void run() {
 while(count.getAndIncrement() number) {
 int from = random.nextInt(account.length);
 int to;
 while ((to = random.nextInt(account.length)) == from)
 int money = random.nextInt(maxMoney);
 doTransfer(from, to, money);
 abstract void doTransfer(int from, int to, int money);
 // 不加锁的转账
 class NonLockRunner extends Runner {
 NonLockRunner(int maxMoney, int number) {
 super(maxMoney, number);
 @Override
 void doTransfer(int from, int to, int money) {
 transfer(from, to, money);
 // synchronized的转账
 class SynchronizedRunner extends Runner {
 SynchronizedRunner(int maxMoney, int number) {
 super(maxMoney, number);
 @Override
 synchronized void doTransfer(int from, int to, int money) {
 transfer(from, to, money);
 // ReentrantLock的转账
 class ReentrantLockRunner extends Runner {
 private final ReentrantLock lock = new ReentrantLock();
 ReentrantLockRunner(int maxMoney, int number) {
 super(maxMoney, number);
 @Override
 void doTransfer(int from, int to, int money) {
 lock.lock();
 try {
 transfer(from, to, money);
 } finally {
 lock.unlock();
 // KeyLock的转账
 class KeyLockRunner extends Runner {
 private final KeyLock Integer lock = new KeyLock Integer 
 KeyLockRunner(int maxMoney, int number) {
 super(maxMoney, number);
 @Override
 void doTransfer(int from, int to, int money) {
 Integer[] keys = new Integer[] {from, to};
 Arrays.sort(keys);
 lock.lock(keys);
 try {
 transfer(from, to, money);
 } finally {
 lock.unlock(keys);
}

 最最重要的测验成果:

 

 

(8线程对100个账户随机转账一共10000次):

       类型:NonLockRunner(不加锁)
       耗时:2482ms
       初始总金额:1000000
       停止总金额:998906(无法确保原子性)

       类型:SynchronizedRunner(加synchronized锁)
       耗时:20872ms
       初始总金额:1000000
       停止总金额:1000000

       类型:ReentrantLockRunner(加ReentrantLock锁)
       耗时:21588ms
       初始总金额:1000000
       停止总金额:1000000

       类型:KeyLockRunner(加KeyLock锁)
       耗时:2831ms
       初始总金额:1000000
       停止总金额:1000000

 

转载:http://blog.csdn.net/icebamboo_moyun/article/details/9391915

版权声明
本文来源于网络,版权归原作者所有,其内容与观点不代表众发娱乐立场。转载文章仅为传播更有价值的信息,如采编人员采编有误或者版权原因,请与我们联系,我们核实后立即修改或删除。

猜您喜欢的文章