简体   繁体   中英

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 . This SimulateDataUpload is locked with lock.lock() . There is a for loop, packaged in a runnable, which updates a progress bar. The onRun of the runnable also locks with 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. Why does the code enter the SimulateDataUpload again?

Code in 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:

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. 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):

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. second one must wait for ending execution of first as method/code is synchronised with same final object. so both Thread s will start but second one will execute in fact after first ends

Took @Shark suggestion and used a reentrant lock . Used the lock's isLocked() features to strengthen the solution. Original code used 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.");
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM