我最近发现使用synchronized不会阻止任何死锁。
E.g。在此代码中:
ArrayList <Job> task;
...
public void do(Job job){
synchronized(tasks){
tasks.add(job);
}
synchronized(this){
notify();
}
}
public void run(){
while(true){
for (int = 0;i<tasks.size();i++){
synchronized(tasks){
Job job = tasks.get(i);
}
//do some job here...
}
synchronized(this){
wait(); //lock will be lost...
notifier = false; //lock will be acquired again after notify()
}
}
}
现在,问题是什么?好吧,如果正在运行的线程没有等待,他将不会看到任何通知(即notify()调用),因此他可能会遇到死锁并且无法处理他收到的任务! (或者他可能来得太晚了......)
因此我实现了这段代码:
private volatile boolean notifier = false;
ArrayList <Job> task;
...
public void do(Job job){
synchronized(tasks){
tasks.add(job);
}
synchronized(this){
notifier = true;
notify();
}
}
public void run(){
while(true){
for (int = 0;i<tasks.size();i++){
synchronized(tasks){
Job job = tasks.get(i);
}
//do some job here...
}
synchronized(this){
if(!notifier){
wait(); //lock will be lost...
notifier = false; //lock will be acquired again after notify()
}
}
}
}
这是正确的还是我错过了什么?它可以更容易吗?
答案 0 :(得分:7)
现在,问题是什么?好吧,如果正在运行的线程没有等待,他将不会看到任何通知(即notify()调用),因此他可能会遇到死锁并且无法处理他收到的任务!
右。这不是“不可靠”的情况,而是语言定义的情况。 notify()
调用不会将通知排队。如果没有线程在等待,那么notify()
将无效地执行任何操作。
可以更容易吗?
是。我会考虑使用BlockingQueue
- LinkedBlockingQueue
应该适合您。一个线程调用从队列调用,另一个可以添加到它。它将为您处理锁定和信号。一旦开始使用,您应该能够删除大部分手写代码。
答案 1 :(得分:0)
我一开始就被你的问题所困扰。 你在线程对象上的同步(this)没有意义。我过去也做这个东西让wait()不会抛出编译错误。
只有等待(任务)才有意义,因为您正在等待并想要获取此资源。
有一个for循环,这是糟糕的设计。在消费者 - 生产者问题。找工作同时删除一份工作。更好地一次取一份工作。
public void do(Job job){
synchronized(tasks){
tasks.add(job);
notify();
}
}
public void run(){
Job job;
while(true){
//This loop will fetch the task or wait for task notification and fetch again.
while (true){
synchronized(tasks){
if(tasks.size()>0){
job = tasks.getTask(0);
break;
}
else
wait();
}
}
//do some job here...
}
}
答案 2 :(得分:0)
结果实际上不是死锁,而是任务/作业本身的饥饿。因为没有线程被“锁定”,所以在另一个线程调用do(Job job)
之前,该任务才会完成。
您的代码几乎是正确的 - 除了在调用wait()
和notify()
时缺少的异常处理。但是您可以将task.size()
放在同步块中,并且可以在孔过程中阻止任务,因为另一个线程删除任务中的作业会让循环失败:
...
while(true){
synchronized(tasks){
for (int = 0;i<tasks.size();i++){ //could be done without synchronisation
Job job = tasks.get(i); //if noone deletes any tasks
}
//do some job here...
}
...
请注意您的代码是阻止的。非阻塞可能更快,看起来像这样:
ArrayList <Job> tasks;
...
public void do(Job job){
synchronized(tasks){
tasks.add(job);
}
}
public void run(){
while(true){
int length;
synchronized(tasks){
length = tasks.size();
}
for (int = 0;i<length;i++){
Job job = tasks.get(i); //can be done without synchronisation if noone deletes any tasks...otherwise it must be within a synchronized block
//do some job here...
}
wait(1); //wait is necessary and time can be set higher but never 0!
}
}
我们可以学到什么?好吧,在非阻塞线程中,不需要notify()
,wait()
和synchronized
。并且设置wait(1)在空闲时甚至不使用更多的CPU(不设置wait(0),因为这将被视为wait()。
但是,请注意,因为使用wait(1)
可能比使用wait()
和notify()
更慢:Is wait(1) in a non-blocking while(true)-loop more efficient than using wait() and notify()?(换句话说:非阻止可能比阻止更慢! )