问题描述
这必须有一个简单的答案,但我只是不知道它是什么...如果我说用Java执行以下操作:
class First{
public void first(){
Second second=new Second();
synchronized(this){
second.second(this);
}
second.second(this);
}
}
如何在调用此方法之前检查Second.second
是否已获得同步锁,如果不是这种情况,可能会引发异常?例如:
class Second{
public void second(First first){
if(!/*want to test that lock obtained for first,but don't kNow how*/){
throw new RuntimeException("Must lock first!");
}
}
}
如果上面的代码不明显,我希望第二次调用Second.second
抛出RuntimeException
。
解决方法
有一种方法:)
public void second(First first) {
if (!Thread.holdsLock(first)) throw new IllegalStateException("Lock required");
}
但是,您不想要这个。
您想要的是什么
public void second(First first) {
synchronized (first) {
// do stuff
}
}
如果一个线程持有一个锁,然后又重新进行同步,则它是免费的,并且不会破坏任何内容:不需要时间,也不会冻结线程。您可以重新获取线程已持有的锁。 Java维护一个计数器。 (锁是可重入的)。
要求呼叫者获得此锁似乎很愚蠢;为什么不自己获取呢?如果呼叫者已经获得它,则没问题。不会浪费时间,代码可以继续起作用。
NB:就代码风格而言,抛出RuntimeException很糟糕,并且在消息中添加感叹号是非常糟糕的(考虑一下;所有异常中超过90%的消息否则将以!结尾,并且正在运行)变得非常烦人以查看日志)。我认为您也不需要像这样的早退括号。因此,如果您必须采用“检查并抛出”样式,则该代码段将编写一些适用于您的修复程序:)
,我希望第二个调用Second.second引发RuntimeException 如果这对上面的代码不明显。
我认为这是一个坏主意。您要么要让First来处理锁,然后再不关心它,要么让Second来处理锁,而不管它是First还是Third。
如果将其与标准库中的类进行比较,则可以查看HashMap与ConcurrentMap。 HashMap是一个非线程安全的类-即,它与示例中的Second相同。 ConcurrentMap是一个“线程安全”类-也就是说,它处理自己的同步操作。
这实际上取决于构成“线程安全”的内容,因此需要更多有关如何使用该类的知识,以了解线程安全的ConcurrentMap方法是否实际上将提供线程安全。
除First以外的任何人都可以访问Second的相同实例,并且您正在从这个角度防范多线程访问吗?如果是这样,那么ConcurrentMap方法可能更合适。在多线程环境中,是否在Second本身上发生了多个操作?如果是这样,手动锁定“第一”会更合适。
使用地图对Second进行多次操作的示例。
Map<Integer,String> map...
... // lets say map has 3 elements by this point and there are 2 threads running.
if (map.size() < 4)
{ // <--- thread may switch here,so both threads are inside the if block
map.put(map.size(),"This map is too small");
// Both threads have put in "This map is too small" to the map.
}
对于这个简单的代码段,无论地图是HashMap还是ConcurrentMap,我们都不能阻止两次添加“此地图太小”。因此,尽管ConcurrentMap提供了“线程安全性”,但此代码实际上并不是线程安全的。因此,我们需要一个外部锁:
...
synchronized (map)
{
if (map.size() < 4)
{
map.add(map.size(),"This map is too small");
}
}
因此,在这种情况下,ConcurrentMap将无济于事,而使用更简单的HashMap将是正确的选择。