[英]Why does my code enter a locked area if the previous code has not released the lock?
我的项目有一个按钮。 按下按钮时,它会执行 function, SimulateDataUpload
。 这个 SimulateDataUpload 被lock.lock()
锁定。 有一个 for 循环,封装在一个 runnable 中,用于更新进度条。 可运行对象的 onRun 也使用lock.lock()
锁定。 当我快速单击该按钮两次时,很明显 for 循环尚未完成迭代,因此不应释放锁。 为什么代码再次进入SimulateDataUpload
?
MainActivity 中的代码:
// defined before onCreate();
private Lock lock = new ReentrantLock();
// defined in onCreate();
simulateDataUploadButton.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
SimulateDataUpload();
}
});
public void SimulateDataUpload()
{
lock.lock();
try
{
Log.i("myTag", "Thread name: " + Thread.currentThread().getName()
+ ". Inside SimulateDataUpload. lock acquired.");
Handler uiOperationsHandler = new Handler();
setUiElements(true);
Runnable simulateDataUploadRunnable = new Runnable()
{
@Override
public void run()
{
lock.lock();
try
{
Log.i("myTag", "Thread name: "
+ Thread.currentThread().getName()
+ ". Inside runnable threadLockObject. lock
acquired.");
for (int i = 0; i < maxProgressBar + 1; i++)
{
try
{
Thread.sleep(500);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
int finalI = i;
uiOperationsHandler.post(new Runnable()
{
@Override
public void run()
{
Log.i("myTag", "Thread name: "
+ Thread.currentThread().getName()
+ ". i: " + finalI
+ ". Inside
uiOperationsHandler.post.");
taskCompletedEventListener.
getCallBackFunction().
onTaskCompletedEvent(finalI ==
maxProgressBar);
}
});
}
}
finally
{
lock.unlock();
Log.i("myTag", "Thread name: "
+Thread.currentThread().getName()
+". lock released.");
}
}
};
Thread simulateDataUploadThread = new
Thread(simulateDataUploadRunnable);
simulateDataUploadThread.start();
}
finally
{
lock.unlock();
Log.i("myTag", "Thread name: " +Thread.currentThread().getName()
+". lock released.");
}
}
这是仅单击一次按钮时的正常 output :
I/myTag: Thread name: main. Inside SimulateDataUpload. lock acquired.
I/myTag: Thread name: Thread-8. Inside runnable threadLockObject. lock acquired.
I/myTag: Thread name: main. lock released.
I/myTag: Thread name: main. i: 0. Inside uiOperationsHandler.post.
I/myTag: Thread name: main. i: 1. Inside uiOperationsHandler.post.
I/myTag: Thread name: main. i: 2. Inside uiOperationsHandler.post.
I/myTag: Thread name: main. i: 3. Inside uiOperationsHandler.post.
I/myTag: Thread name: Thread-8. lock released.
这是单击两次按钮时的 output。 我不明白为什么我要第二次输入“Inside SimulateDataUpload ...”,因为锁定的 object 的可运行对象尚未释放(尚未完成其 for 循环迭代):
I/myTag: Thread name: main. Inside SimulateDataUpload. lock acquired.
I/myTag: Thread name: main. lock released.
I/myTag: Thread name: Thread-11. Inside runnable threadLockObject. lock acquired.
I/myTag: Thread name: main. i: 0. Inside uiOperationsHandler.post.
I/myTag: Thread name: Thread-11. lock released.
I/myTag: Thread name: main. Inside SimulateDataUpload. lock acquired.
I/myTag: Thread name: main. lock released.
I/myTag: Thread name: Thread-12. Inside runnable threadLockObject. lock acquired.
I/myTag: Thread name: main. i: 1. Inside uiOperationsHandler.post.
I/myTag: Thread name: main. i: 2. Inside uiOperationsHandler.post.
I/myTag: Thread name: main. i: 3. Inside uiOperationsHandler.post.
I/myTag: Thread name: main. i: 0. Inside uiOperationsHandler.post.
I/myTag: Thread name: main. i: 1. Inside uiOperationsHandler.post.
I/myTag: Thread name: main. i: 2. Inside uiOperationsHandler.post.
I/myTag: Thread name: Thread-12. lock released.
I/myTag: Thread name: main. i: 3. Inside uiOperationsHandler.post.
由于 logs synchronised
关键字的作用就像一个魅力,您已经创建了两个Thread
并启动它们,从未中断,没有保留参考。 第二个必须等待第一个的结束执行,因为方法/代码与相同的final
object 同步。 所以两个Thread
都会启动,但第二个实际上会在第一个结束后执行
接受了@Shark 的建议并使用了reentrant lock
。 使用锁的isLocked()
功能来加强解决方案。 使用synchronized (lockObject)
的原始代码。 单独的线程过早地释放了锁,因此多次单击按钮会产生奇怪的结果。 如果有人能解释为什么它会这样,我肯定很想知道。
无论如何,这是可行的解决方案:
//synchronize (myObject) doesn't work in this example... so using a ReentrantLock.
private ReentrantLock lock = new ReentrantLock();
private final int maxProgressBar = 6;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// ... Other stuff defined here
simulateDataUploadButton.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
// Problem: The user can click multiple times and our runnable will be concurrently run in multiple different threads
// Used a lock to prevent this.
// 1. SimulateDataUpload. Lock Acquired by Main Thread. A runnable package is created and a new thread with that package is started. Lock released by Main Thread.
// 2. New thread runs and tries to acquire the lock, but is blocked until SimulateDataUpload releases the lock.
// 3. The new Thread acquires the lock.
// 4. The new Thread finishes the for loop and releases the lock.
SimulateDataUpload();
}
});
}
public void SimulateDataUpload()
{
if(lock.isLocked())
{
// If we are in the middle of an operation with the lock, return.
return;
}
else
{
lock.lock();
}
try
{
Log.i("myTag", "Thread name: " + Thread.currentThread().getName() + ". Inside SimulateDataUpload. lock acquired.");
Handler uiOperationsHandler = new Handler();
setUiElements(true);
Runnable simulateDataUploadRunnable = new Runnable()
{
@Override
public void run()
{
while (lock.isLocked())
{
// If we call this runnable with simulateDataUploadThread.start(), we need it to complete. So just wait for whatever has a lock to release the lock.
}
lock.lock();
try
{
Log.i("myTag", "Thread name: " + Thread.currentThread().getName() + ". Inside runnable threadLockObject. lock acquired.");
for (int i = 0; i < maxProgressBar + 1; i++)
{
try
{
Thread.sleep(500);
}
catch (InterruptedException e)
{
Log.i("myTag", "Thread name: " + "exception: " +e);
}
// Fire Event. Use a handler because we are going to be updating UI elements.
int finalI = i;
uiOperationsHandler.post(new Runnable()
{
@Override
public void run() {
Log.i("myTag", "Thread name: " + Thread.currentThread().getName() + ". i: " + finalI + ". Inside uiOperationsHandler.post.");
taskCompletedEventListener.
getCallBackFunction().
onTaskCompletedEvent(finalI == maxProgressBar);
}
});
}
}
finally
{
lock.unlock();
Log.i("myTag", "Thread name: " +Thread.currentThread().getName() +". lock released.");
}
}
};
Thread simulateDataUploadThread = new Thread(simulateDataUploadRunnable);
simulateDataUploadThread.start();
}
finally
{
lock.unlock();
Log.i("myTag", "Thread name: " +Thread.currentThread().getName() +". lock released.");
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.