锁算法之指数后退

377 阅读2分钟

简介:

这篇文章主要讲的是如何优化处理前面所讲的getAndSet()带来的流量风暴的问题。

前置知识

争用:指多个线程试图同时获取一个锁。高争用意味着存在大量正在争用的线程,低争用的意思与高争用相反。

使用场景

当存在高争用锁的情况时我们应该采用一种回避的情形(请结合前面所讲的TTASLock来理解),因为高争用的情况下获取锁的机会非常小,从而导致执行了多次getAndSet()方法导致总线流量增加。 相反如果我们让线程“后退一段时间”给正在竞争的线程有结束的机会,将会更加有效。 对于一些锁算法来说“后退”是一种常用的方法

如何控制回退时长?

一种好的准则:不成功的尝试次数越多,发生争用的可能性就越高,线程需要后退的时间就越长。

简单的实现Demo

public class BackOff {
    final int minDelay, maxDelay;
    int limit;
    final Random random;

    public BackOff(int min, int max) {
        minDelay = min;
        maxDelay = max;
        limit = minDelay;
        random = new Random();
    }

    public void backoff() throws InterruptedException {
        int delay = random.nextInt(limit);
        limit = Math.min(maxDelay, 2 * limit);
        Thread.sleep(delay);
    }
}
public class BackoffLock implements Lock {
    private AtomicBoolean state = new AtomicBoolean(false);
    private static final int MIN_DELAY = ...;
    private static final int MAX_DELAY = ...;

    @SneakyThrows
    @Override
    public void lock() {
        BackOff backoff = new BackOff(MIN_DELAY, MAX_DELAY);
        while (true) {
            while (state.get()) {
            }
            if (!state.getAndSet(true)) {
                return;
            } else {
                backoff.backoff();
            }
        }
    }
  
    @Override
    public void unlock() {
        state.set(false);
    }
 }

注意事项

MIN_DELAY 最小初始延迟时间(不应设置太小,太小的话没有实际意义)、MAX_DELAY 最大延迟时间 这个锁的性能和MIN_DELAY、MAX_DELAY取值相关同时也和系统的结构存在关联。

缺点

移植性差,因为它的性能与系统结构存在着关联,更好的实现方式为后面的队列锁

后话

这些东西在我们现在的程序中基本很少会自己去写,因为jdk已经自带了强大的锁的实用类,但是这里还是要记录下来,因为这些程序我们可以明确的了解的锁的实现和它的一些原理及优化。