这是一个公平的比喻吗?我想我可以想到你有使用锁的一些场景,但我不确定它们是否必要。
例如,这是我最近写的一个循环,用于执行等待线程更新列表。如果这是一个糟糕的java,我很抱歉,因为我是一个原生的linux kernel-ite:
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的伎俩我怎么能做到这一点。
答案 0 :(得分:3)
好吧,同步块比ReentrantLock这样的类早于许多年;引入该包中的类以提供比该语言之前提供的Java更复杂和更高级的功能 - 尽管只有在非常特定的情况下才需要这些功能。
但在这种特殊情况下,我会说使用同步块(等待(N)而不是睡眠(N)!)会更优雅。我理解你的比喻,我会说有时它持有;当然,对于这种普通的情况,使用同步块就像在C ++中使用RAII模式一样 - 这是确保在需要时清理内容的最清晰方法。
答案 1 :(得分:1)
通常,设计行为(并且执行)良好的并发数据结构是一项非常棘手的任务。如果你没有使用低级工具自己实现所有东西,而是使用标准库中提供的更高级抽象,那么它被认为是一件好事(.mm)。
对于您的任务,我宁愿使用BlockingQueue或BlockingDeque来处理同步。
答案 2 :(得分:0)
同步块总是在本质上正确嵌套,而可以滥用锁定并忘记解锁。但是,你读到锁的第一件事就是总是在try / finally结构中使用它们,如下所示:
lock.lock();
try {
// Do things...
}
finally {
lock.unlock();
}
这是强烈推荐的模式,可确保您的锁定为同步块。
锁定比同步块有几个优点:
所以,恕我直言,锁更像是一把更大的枪:更强大,但如果你不知道如何使用它们也会更危险。
答案 3 :(得分:-1)
相同代码的修改版本,更优化的IMO :)为了回答你的问题,这个比喻是错误的。由于他们允许控制流逃跑的方式,得到了一个坏名声,导致噩梦。锁不允许控制以相同的方式逃脱。那么,你可能会问,如果我获得一把锁并拒绝释放它会发生什么。同步块无法实现。理想的模式是使用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;
}
}
}