【问题标题】:What exactly gets locked when using APIs in java.util.concurrent.Locks?在 java.util.concurrent.Locks 中使用 API 时究竟会锁定什么?
【发布时间】:2019-03-12 12:36:52
【问题描述】:

这可能是一个非常愚蠢的问题。我正在阅读包java.util.concurrent.atomicjava.util.concurrent.locks 中的接口和类的文档。我到处都能找到

void lock() – 获取锁(如果可用);如果锁不可用,则线程将被阻塞,直到锁被释放

我不确定的是,究竟是什么资源被锁定了?代码sn-p示例展示

Lock lock = new ReentrantLock(); 
lock.lock();
try {
    // access to the shared resource
} finally {
    lock.unlock();
}

lock() 的调用下使用的任何东西都会被锁定吗?在到达那条线之前,JVM 是如何知道这一点的?如果lock()unlock() 之间有多个资源怎么办?我想我有这个问题是因为我在阅读 synchronization 之后正在阅读这个问题,并且它有非常具体的方式来说明要锁定的内容 - 比如:synchronized(resourceReferenceThatNeedsToBeLocked)

我研究了很多,但找不到这个问题的答案。

【问题讨论】:

  • 没有资源被锁定,但执行某些指令的线程被阻止继续。线程只不过是一系列指令的独立执行。多个线程可能执行相同或不同的指令,这并不重要。如果他们想访问由同一个锁保护的关键部分,他们将竞争锁。无法获得锁的线程将等到锁再次可用并再次竞争它。这可能会导致线程拥塞,在最坏的情况下会导致死锁。

标签: java multithreading


【解决方案1】:

您可以将您的代码视为同步的优化版本。你在你的锁对象上“同步”,但是以一种更有效的方式。

请注意,当您使用同步时,不能保证 synchronized 块内使用的资源。您只是锁定了一个特定对象,它可能与您在 synchronized 块内使用的资源相同,也可能不同。本质上,不管是锁还是synchronized,你只是在说“确保没有其他线程可以访问该块内的代码(或由同一锁保护的其他代码或同一实例上的‘同步’),直到我完了”。

要理解的关键是,无论是锁定还是同步,您都在保护 代码块 免受并发访问。块内的代码可以访问一个或多个不同的资源;如果在其他地方使用相同的资源,则需要使用相同的锁来保护对它们的访问或在相同的实例上同步以确保安全。

【讨论】:

  • 你能补充更多细节吗?为什么我要同步一个锁对象,而不是我的资源?
  • 是不是说lock()unlock()之间的所有可变资源都被当前线程锁定了?
  • 添加了更多说明。
  • Re,“……但以更有效的方式。”那将是一个实现细节。没有什么能阻止 JRE 以最有效的方式实现 synchronized。使用显式 Lock 对象为开发人员提供了更大的灵活性,但它们本身并不是更快。
【解决方案2】:

锁总是与数据相关联。如果没有数据,同步是没有意义的。你有对象线程、锁、条件等等。然后你就有了在这些对象的帮助下同步的数据结构。你需要完整的例子。在下面的示例中,我正在同步一个队列,以便添加到其中的数据始终保持同步。当然它是从不同线程添加到队列中的

import java.util.Deque;
import java.util.LinkedList;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;


public class BackgroundWorker {

    private Worker worker;
    private StringBuilder allData;

    @Before
    public void setUp() throws Exception {
        worker = new Worker(allData); // start databse worker
        worker.start();
    }

    @After
    public void tearDown() throws Exception {
        worker.stop();
    }

    public void logValue(final String data) {
        final LogValue logValue = new LogValue(data);
        worker.queue(logValue);
    }

    @Test
    public void test() {
        // very dummy, NOT multithreaded test 
        for (int i = 0; i < 10; i++) {
            logValue("Some data " + i);
        }
    }

    private static class Worker implements Runnable {

        private static final int MAX_QUEUE_SIZE = 1000;

        private final Deque<Job> queue = new LinkedList<Job>();
        private final Lock lock = new ReentrantLock();
        private final Condition jobAdded = lock.newCondition();
        private final Condition jobRemoved = lock.newCondition();
        private final StringBuilder dataSource;
        private final AtomicBoolean running = new AtomicBoolean(false);
        private Thread thread = null;

        Worker(final StringBuilder dataSource) {
            this.dataSource = dataSource;
        }

        @Override
        public void run() {

            processing: for (;;) {
                Job job;
                lock.lock();
                try {
                    while (null == (job = queue.pollFirst())) {
                        if (!running.get()) break processing;

                        try {
                            jobAdded.await();
                        } catch (InterruptedException ie) {
                            ie.printStackTrace();
                        }
                    }
                    jobRemoved.signal();
                }
                finally {
                    lock.unlock();
                }
                job.run(dataSource);
            }

        }

        void start() {
            lock.lock();
            try {
                if (running.getAndSet(true)) return; // already running
                thread = new Thread(this, "Database worker");
                thread.start();
            }
            finally {
                lock.unlock();
            }
        }

        void stop() {
            Thread runningThread;
            lock.lock();
            try {
                if (!running.getAndSet(false)) return; // already stopped
                runningThread = thread;
                thread = null;
                jobAdded.signal();
            }
            finally {
                lock.unlock();
            }

            // wait for the thread to finish while not holding a lock
            try {
                runningThread.join(2000); // we give it 2 seconds to empty its queue
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            runningThread.interrupt(); // we interrupt it just in case it hasn't finished yet
        }

        void queue(final Job job) {
            if (!running.get()) throw new IllegalStateException("Worker thread is not running");
            lock.lock();
            try {
                while (queue.size() >= MAX_QUEUE_SIZE) {
                    try {
                        jobRemoved.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                queue.addLast(job);
                jobAdded.signal();
            }
            finally {
                lock.unlock();
            }
        }
    }

    private static interface Job {

        void run(StringBuilder dataSource);
    }

    private static class LogValue implements Job {

        final String myData;

        LogValue(final String data) {
            this.myData = data;
        }

        @Override
        public void run(final StringBuilder dataSource) {
            dataSource.append(this.myData);
        }
    }

}

【讨论】:

    【解决方案3】:

    回答我的问题:

    被锁定的对象是引用变量lock所引用的对象。在本例中为 ReentrantLock 对象。

    需要注意的重要一点:

    上面的代码可能具有误导性。在方法中创建新的lock 对象将由各自的线程完成,并且各自的线程只会锁定在其堆栈中创建的对象。如果您想锁定特定实例的变量或方法,该实例应该有自己的lock 对象,并且只能使用同一个锁定对象进行同步。

    请参阅this 问题了解更多信息。请参阅this 相关锁的文档。

    【讨论】:

      猜你喜欢
      • 2011-08-29
      • 2010-12-29
      • 2023-04-01
      • 2013-05-27
      • 2019-12-31
      • 2015-12-25
      • 1970-01-01
      • 2023-03-29
      • 2019-10-12
      相关资源
      最近更新 更多