簡體   English   中英

如果之前的代碼沒有解鎖,為什么我的代碼進入了鎖定區域?

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

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM