在java.util.concurrent.Locks中使用API时,到底锁定了什么?
这可能是一个非常愚蠢的问题。我正在阅读java.util.concurrent.atomic和java.util.concurrent.locks包中接口和类的文档。我到处都能找到 void lock–获取锁(如果可用);如果锁不可用,线程将被阻塞,直到释放锁 我不确定的是,到底是什么资源被锁定了?代码片段示例显示在java.util.concurrent.Locks中使用API时,到底锁定了什么?,java,multithreading,Java,Multithreading,这可能是一个非常愚蠢的问题。我正在阅读java.util.concurrent.atomic和java.util.concurrent.locks包中接口和类的文档。我到处都能找到 void lock–获取锁(如果可用);如果锁不可用,线程将被阻塞,直到释放锁 我不确定的是,到底是什么资源被锁定了?代码片段示例显示 Lock lock = new ReentrantLock(); lock.lock(); try { // access to the shared resource }
Lock lock = new ReentrantLock();
lock.lock();
try {
// access to the shared resource
} finally {
lock.unlock();
}
在lock的调用下使用的任何东西都会被锁定吗?JVM是如何知道这一点的?如果在锁定和解锁之间有多个资源呢?我想我有这个问题,因为我是在读了同步之后读到的,它有一种非常具体的方式来说明要锁定什么,比如:synchronizedresourceReferenceThatNeedsToBeLocked
我做了很多研究,但没有找到这个问题的答案 您可以将您的代码看作是synchronized的优化版本。您正在锁定对象上进行同步,但以更有效的方式进行 请注意,当您使用synchronized时,对于synchronized块内部使用的资源没有任何保证。您只是锁定了一个特定的对象,该对象可能与您在同步块中使用的资源相同,也可能不同。本质上,不管是锁还是同步,您只是说确保在我完成之前,没有其他线程可以访问此块内同一实例上由同一锁或“同步”保护的代码或其他代码
要理解的关键是,无论是锁定还是同步,您都在保护一块代码不被并发访问。块内的代码可以访问一个或多个不同的资源;如果在其他地方使用相同的资源,则需要使用相同的锁来保护对它们的访问,或者在同一实例上进行同步,以确保安全。锁始终与数据关联。如果没有数据,同步就没有意义。您有对象线程、锁、条件等。然后,您就有了数据结构,它在这些对象的帮助下是同步的。你需要完整的例子。在下面的示例中,我正在同步一个队列,所以添加到队列中的数据总是同步的。当然,它是从不同的线程添加到队列中的
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);
}
}
}
回答我的问题:
锁定的对象是引用变量lock所引用的对象。在本例中,是一个ReentrantLock对象
需要注意的一点是:
上面的代码可能会产生误导。在方法中创建一个新的锁对象将由相应的线程完成,并且相应的线程将只锁定在其堆栈中创建的对象。如果要锁定特定实例的变量或方法,则该实例应该有自己的锁定对象,并且只有相同的锁定对象应用于同步
有关更多信息,请参阅问题。请参阅相关锁的文档。您能添加更多详细信息吗?为什么我要同步一个锁对象,而不是我的资源?这是否意味着,锁和解锁之间的所有可变资源都被当前线程锁定?添加了更多的说明。Re,…但以更有效的方式。这将是一个实施细节。没有什么可以阻止JRE以最有效的方式实现同步。使用显式锁对象为开发人员提供了更大的灵活性,但它们的速度并不是天生就快。没有资源被锁定,但执行某些指令的线程被阻止继续。线程只不过是一系列指令的独立执行。多个线程可能执行相同或不同的指令,这并不重要。如果他们想要访问由同一个锁保护的关键区域,他们将竞争该锁。无法获得锁的线程将等待锁再次可用并再次竞争。这可能会导致螺纹啮合,在最坏的情况下会导致死锁。