我正在寻找类似于实现java.lang.AutoCloseable接口的东西,其中生成了指示Resource leak: 'xxxx' is never closed
的编译器警告。
这个用例是在java中的Synchronized Collection的包装中。包装器有一个内部信号量,以防止同时修改集合。
它允许集合上的原子操作,在这种情况下,信号量是在内部获取和释放的。它还允许从外部获取锁,提供可以在集合上执行操作的唯一键。密钥必须在"事务"。
结束时释放我的目标是在获取锁定时创建编译器警告,而不是在同一方法中释放,以防止死锁。另一种可以防止这种情况的设计方案也是可以接受的。
这是一个有趣的小问题,所以我很欣赏它的任何见解。
答案 0 :(得分:2)
正如你所说
另一种可以防止这种情况的设计解决方案也是可以接受的。
所以这里是:作为替代设计解决方案,使用功能编程。
为什么不首先防止错误发生?
而不是发现错误。缺少源代码,我对您的代码做了一些假设:
Semaphore
是您的班级(或界面),为您的SynchronizedCollection
提供信号量。Semaphore
提供了两种方法obtain()
和release()
。您实际面临的问题是 State resp的问题。 状态变化导致时间耦合。必须按顺序调用obtain()
和release()
。您可以使用 Functional Programming 中的元素作为替代设计。
Semaphore
目前看起来像这样:
public class Sempahore {
// ...
public void obtain() {
// Lock code
}
public void release() {
// Release code
}
}
Semaphore
用户目前看起来像这样:
semaphore.obtain();
// Code protected by the Sempahore.
semaphore.release();
解决方案是将obtain()
和release()
组合成一个函数,该函数将代码保护为其参数。此技术也称为传递块,或者更正式地称为高阶函数 - 一个将另一个函数作为参数或返回另一个函数的函数。
Java也有函数指针,不是直接的,而是间接地通过对接口的引用。从Java 8开始,只有一个抽象方法的接口甚至称为功能接口,Java 8为此提供了可选注释@FunctionalInterface
。
因此,您的class Sempahore
可能会显示如下:
public class Semaphore {
// ...
private void obtain() {
// Lock code
}
private void release() {
// Release code
}
public <V> void protect(final Callable<V> c) throws Exception {
obtain();
try {
return c.call();
} finally {
release();
}
}
}
调用程序在Java 7及更早版本中看起来像这样:
semaphore.protect(new Callable<Object>() {
public Object call() {
// Code protected by the Semaphore.
}
});
在Java 8及更新版本中,代码也可能如下所示:
semaphore.protect(() -> {
// Code protected by the Semaphore.
});
Java的一个方面在这种情况下完全糟糕:异常处理。通过函数式编程,迫切需要解决这个问题,但Oracle没有。我仍然希望使用Java 9,但这并不会帮助那些像java.util.stream
那样已经破坏了API的API。 Java 8仍然维护 handle-or-declare -rule的已检查异常,但函数式编程并没有很好地考虑到这一点。
有一些解决方法:
Runnable
。Callable
接口,该接口声明了异常的类型参数。我打赌使用Runnable
是直截了当且不言自明的,因此我不会详细说明。
使用您自己的Callable
界面版本如下所示:
public interface ProtectedCode<V,E> {
V call() throws E;
}
public class Semaphore {
// ...
private void obtain() {
// Lock code
}
private void release() {
// Release code
}
public <V, E> void protect(final ProtectedCode<V, E> c) throws E {
obtain();
try {
return c.call();
} finally {
release();
}
}
}
现在你不需要乱用Exception,只要限制(因为它只能反映一种类型,而不是类型集)类型参数E
的类型推断导致编译器中的合理结果
如果您希望对用户非常友好,您实际上可以提供protect
方法的三种变体:
public void protect(final Runnable r)
public <V> V protect(final Callable<V> c) throws Exception
public <V,E> V protect(final ProtectedCode<V,E> c) throws E
答案 1 :(得分:1)
为了创建编译器警告,您需要扩展Eclipse编译器。
另一种解决方案是在软件质量分析系统中创建自定义检查,例如 Teamscale 或 SonarQube。自定义检查执行代码的静态分析(通常基于丰富了语义信息的抽象语法树,并创建问题,他们检测到狡猾的代码。问题显示在质量分析系统的用户界面上。 Eclipse插件允许在Eclipse中集成系统,以便可以在那里列出问题。
答案 2 :(得分:0)
虽然@Christian Hujer确实提供了一个可靠的解决方案,但我选择了另一条一直运作良好的路线。
有一个包装类&#34;资源&#34;在SynchronizedCollection周围包含:
上面描述的类足以为集合提供足够的保护,但是如果锁没有被释放,我想要的是编译器警告。
要做到这一点,有一个&#34; ResourceManager&#34;它实现了java.lang.AutoCloseable
这堂课:
在需要对资源执行多个操作的任何地方创建资源管理器,如果未在任何特定代码路径上调用close(),则会生成编译器警告。此外,在java 7+中,可以在try-with-resource块中创建管理器,并且无论块中发生什么,锁都会自动释放。