这是一个公平的类比吗?我想我可以想到一些场景,你可以使用锁,但我不确定它们是否必要。
例如,下面是我最近编写的一个循环,用于等待线程更新列表。我很抱歉,如果这是可怕的java,因为我是一个本地的linux内核:ReentrantLock lock;
...
while(true) {
lock.lock();
int size = queue.size();
if (size == 0) {
try {
lock.unlock();
Thread.sleep(3600000);
continue;
} catch (InterruptedException e) {
lock.lock();
size = queue.size();
if (size == 0) {
lock.unlock();
continue;
}
}
}
VPacket p = queue.getFirst();
lock.unlock();
return p;
}
其中VPacket
是我正在编写的特定协议的数据包。
当我思考Collections.synchonizedList class
时,这个类比出现在脑海中,思考如何在没有这种类似c的技巧的情况下做到这一点。
同步块比ReentrantLock这样的类早很多年;该包中的类被引入,以提供一些比Java语言先前提供的更复杂、更高级的功能——尽管其中许多功能仅在非常特殊的情况下才需要。
但是在这个特定的情况下,我会说使用同步块(并且等待(N)而不是睡眠(N)!)会更优雅。我理解你的比喻,我想说有时是成立的;当然,对于这种普通的情况,使用同步块就像在c++中使用RAII模式一样——这是确保在需要时清理东西的最清晰的方法。
一般来说,设计行为(和执行)良好的并发数据结构是一项非常棘手的任务。如果您不使用低级工具自己实现所有的东西,而是使用标准库中可用的更高级的抽象,这被认为是一件好事(tm)。
对于您的任务,我宁愿使用BlockingQueue或BlockingDeque来为我处理同步
同步块本质上总是正确嵌套的,而您可以误用锁而忘记解锁。然而,你读到的关于锁的第一件事是总是在try/finally结构中使用它们,像这样:
lock.lock();
try {
// Do things...
}
finally {
lock.unlock();
}
这是一个强烈推荐的模式,确保你的锁像同步块一样工作。 锁与同步块相比有以下几个优点: 所以,我的意思是,锁更像是一把更大的枪:更强大,但如果你不知道如何使用它们,也会更危险。它们比同步块更有效,尽管我读到在最近的JVM中已经修复了这个问题。为了提高并发性,一些锁可以分为读锁和写锁。
相同代码的修改版本,更优化的IMO:)回答你的问题,类比是错误的。Gotos的名声很差,因为他们允许控制流逃逸,导致噩梦。锁在同样的意义上不允许控制逃逸。你可能会问,如果我获得了一个锁却拒绝释放它会发生什么。同步块是不可能的。理想的模式是使用try-finally。顺便说一句,并发编程本身就是一场噩梦,无法增强或减少。
while(true) {
if(queue.size()==0){
Thread.sleep(36000);
}
lock.lock();
Vpacket p = null;
try {
p = null;
if(queue.size()!=0) {
p = queue.pop();
}
} finally {
lock.unlock();
if(p!=null) {
return p;
}
}
}