简体   繁体   English

如果之前的代码没有解锁,为什么我的代码进入了锁定区域?

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

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