繁体   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