[英]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.