[英]Why does my code enter a locked area if the previous code has not released the lock?
My project has a button.我的项目有一个按钮。 When the button is pushed, it executes a function,
SimulateDataUpload
.按下按钮时,它会执行 function,
SimulateDataUpload
。 This SimulateDataUpload is locked with lock.lock()
.这个 SimulateDataUpload 被
lock.lock()
锁定。 There is a for loop, packaged in a runnable, which updates a progress bar.有一个 for 循环,封装在一个 runnable 中,用于更新进度条。 The onRun of the runnable also locks with
lock.lock()
.可运行对象的 onRun 也使用
lock.lock()
锁定。 When I click the button two times quickly, it's clear the for loop has not finished iterating, so the lock shouldn't be released.当我快速单击该按钮两次时,很明显 for 循环尚未完成迭代,因此不应释放锁。 Why does the code enter the
SimulateDataUpload
again?为什么代码再次进入
SimulateDataUpload
?
Code in MainActivity: 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.");
}
}
Here is the normal output when button is only clicked once:这是仅单击一次按钮时的正常 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.
Here is the output when the button is clicked twice.这是单击两次按钮时的 output。 I don't understand why I'm entering "Inside SimulateDataUpload..." a second time when it's clear the runnable with the locked object hasn't released the lock yet (hasn't finished its for loop iterations):
我不明白为什么我要第二次输入“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.
due to logs synchronised
keyword worked like a charm, you have created two Thread
s and started them, never interrupted, no reference kept.由于 logs
synchronised
关键字的作用就像一个魅力,您已经创建了两个Thread
并启动它们,从未中断,没有保留参考。 second one must wait for ending execution of first as method/code is synchronised with same final
object.第二个必须等待第一个的结束执行,因为方法/代码与相同的
final
object 同步。 so both Thread
s will start but second one will execute in fact after first ends所以两个
Thread
都会启动,但第二个实际上会在第一个结束后执行
Took @Shark suggestion and used a reentrant lock
.接受了@Shark 的建议并使用了
reentrant lock
。 Used the lock's isLocked()
features to strengthen the solution.使用锁的
isLocked()
功能来加强解决方案。 Original code used synchronized (lockObject)
.使用
synchronized (lockObject)
的原始代码。 The separate thread was prematurely releasing the lock, so clicking on button multiple times would give strange results.单独的线程过早地释放了锁,因此多次单击按钮会产生奇怪的结果。 If anyone can explain why it behaved that way, I'm definitely curious to know.
如果有人能解释为什么它会这样,我肯定很想知道。
Anyway, here is the working solution:无论如何,这是可行的解决方案:
//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.