使用Java 8和Lambda简化ReadWriteLock

考虑到旧版Java代码,无论您在哪里看,带有lambda表达式的Java 8绝对可以提高质量和可读性。 今天,让我们看一下ReadWriteLock以及如何使它使用起来更简单。 假设我们有一个称为Buffer的类,该类可以记住队列中的最后几条消息,对旧消息进行计数并丢弃。 实现非常简单:

public class Buffer {
 
    private final int capacity;
    private final Deque<String> recent;
    private int discarded;
 
    public Buffer(int capacity) {
        this.capacity = capacity;
        this.recent = new ArrayDeque<>(capacity);
    }
 
    public void putItem(String item) {
        while (recent.size() >= capacity) {
            recent.removeFirst();
            ++discarded;
        }
        recent.addLast(item);
    }
 
    public List<String> getRecent() {
        final ArrayList<String> result = new ArrayList<>();
        result.addAll(recent);
        return result;
    }
 
    public int getDiscardedCount() {
        return discarded;
    }
 
    public int getTotal() {
        return discarded + recent.size();
    }
 
    public void flush() {
        discarded += recent.size();
        recent.clear();
    }
 
}

现在我们可以putItem() ,但是内部recent队列将仅保留最后一个capacity元素。 但是,它也记住必须丢弃多少项以避免内存泄漏。 该类工作正常,但仅在单线程环境中有效。 我们使用不是线程安全的ArrayDeque和非同步的int 。 尽管对int读写是原子的,但不能保证更改在不同线程中可见。 同样,即使我们将线程安全的BlockingDequeAtomicInteger一起使用,我们仍然处于竞争状态的危险中,因为这两个变量彼此不同步。

一种方法是synchronize所有方法 ,但这似乎很严格。 此外,我们怀疑读取的数量大大超过写入的数量。 在这种情况下, ReadWriteLock是绝佳的选择。 它实际上包括两个锁-一个用于读取,一个用于写入。 实际上,它们都为同一把锁竞争,而同一把锁可以同时由一个作者或多个读者获得。 因此,当没有人在写并且只有写者偶尔阻塞所有读者时,我们可以进行并发读取。 使用synchronized将始终阻止所有其他对象,无论他们做什么。 ReadWriteLock的可悲部分是它引入了许多样板。 您必须显式打开一个锁,并记住在finally块中对其进行unlock() 。 我们的实现变得难以阅读:

public class Buffer {
 
    private final int capacity;
    private final Deque<String> recent;
    private int discarded;
 
    private final Lock readLock;
    private final Lock writeLock;
 
 
    public Buffer(int capacity) {
        this.capacity = capacity;
        recent = new ArrayDeque<>(capacity);
        final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
        readLock = rwLock.readLock();
        writeLock = rwLock.writeLock();
    }
 
    public void putItem(String item) {
        writeLock.lock();
        try {
            while (recent.size() >= capacity) {
                recent.removeFirst();
                ++discarded;
            }
            recent.addLast(item);
        } finally {
            writeLock.unlock();
        }
    }
 
    public List<String> getRecent() {
        readLock.lock();
        try {
            final ArrayList<String> result = new ArrayList<>();
            result.addAll(recent);
            return result;
        } finally {
            readLock.unlock();
}
 
    public int getDiscardedCount() {
        readLock.lock();
        try {
            return discarded;
        } finally {
            readLock.unlock();
        }
    }
 
    public int getTotal() {
        readLock.lock();
        try {
            return discarded + recent.size();
        } finally {
            readLock.unlock();
        }
    }
 
    public void flush() {
        writeLock.lock();
        try {
            discarded += recent.size();
            recent.clear();
        } finally {
            writeLock.unlock();
        }
    }
 
}

这是在八月8日之前完成的方式。有效,安全且丑陋。 但是,使用lambda表达式,我们可以将横切关注点包装在这样的实用程序类中:

public class FunctionalReadWriteLock {
 
    private final Lock readLock;
    private final Lock writeLock;
 
    public FunctionalReadWriteLock() {
        this(new ReentrantReadWriteLock());
    }
 
    public FunctionalReadWriteLock(ReadWriteLock lock) {
        readLock = lock.readLock();
        writeLock = lock.writeLock();
    }
 
    public <T> T read(Supplier<T> block) {
        readLock.lock();
        try {
            return block.get();
        } finally {
            readLock.unlock();
        }
    }
 
    public void read(Runnable block) {
        readLock.lock();
        try {
            block.run();
        } finally {
            readLock.unlock();
        }
    }
 
    public <T> T write(Supplier<T> block) {
        writeLock.lock();
        try {
            return block.get();
        } finally {
            writeLock.unlock();
        }
public void write(Runnable block) {
        writeLock.lock();
        try {
            block.run();
        } finally {
            writeLock.unlock();
        }
    }
 
}

如您所见,我们包装了ReadWriteLock并提供了一组可使用的实用程序方法。 原则上,我们希望传递RunnableSupplier<T> (具有单个T get()方法的接口),并确保调用它时已被适当的锁包围。 我们可以编写完全相同的包装器类,而无需使用lambda,但是使用它们可以大大简化客户端代码:

public class Buffer {
 
    private final int capacity;
    private final Deque<String> recent;
    private int discarded;
 
    private final FunctionalReadWriteLock guard;
 
    public Buffer(int capacity) {
        this.capacity = capacity;
        recent = new ArrayDeque<>(capacity);
        guard = new FunctionalReadWriteLock();
    }
 
    public void putItem(String item) {
        guard.write(() -> {
            while (recent.size() >= capacity) {
                recent.removeFirst();
                ++discarded;
            }
            recent.addLast(item);
        });
    }
 
    public List<String> getRecent() {
        return guard.read(() -> {
            return recent.stream().collect(toList());
        });
    }
 
    public int getDiscardedCount() {
        return guard.read(() -> discarded);
    }
 
    public int getTotal() {
        return guard.read(() -> discarded + recent.size());
    }
 
    public void flush() {
        guard.write(() -> {
            discarded += recent.size();
            recent.clear();
        });
    }
 
}

看看我们如何调用guard.read()guard.write()传递应该受到保护的代码段? 看起来很整洁。 顺便说一句,您是否注意到我们如何使用stream()将任何集合转换为任何其他集合(在这里: Deque into List stream() ? 现在,如果我们提取几个内部方法,则可以使用方法引用来进一步简化lambda:

public void flush() {
    guard.write(this::unsafeFlush);
}
 
private void unsafeFlush() {
    discarded += recent.size();
    recent.clear();
}
 
public List<String> getRecent() {
    return guard.read(this::defensiveCopyOfRecent);
}
 
private List<String> defensiveCopyOfRecent() {
    return recent.stream().collect(toList());
}

这只是利用lambda表达式来改进现有代码和库的众多方法之一。 我们真的很高兴他们终于进入Java语言了,同时已经出现在其他数十种JVM语言中。

翻译自: https://www.javacodegeeks.com/2014/03/simplifying-readwritelock-with-java-8-and-lambdas.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值