Java 每个要同步的变量有一个ReentrantReadWriteLock实例?

Java 每个要同步的变量有一个ReentrantReadWriteLock实例?,java,multithreading,reentrantreadwritelock,Java,Multithreading,Reentrantreadwritelock,我将用一些全局变量并行化一些代码。 我将使用ReentrantReadWriteLock。 我是否理解正确,我需要为每个变量提供一个自己的ReentrantReadWriteLock实例,以确保线程安全 我的意思是,当我有两个列表,其中每个线程都可以附加一个项目,而所有线程有时都在从该列表中读取项目。 在这种情况下,我将实现如下内容: private static String[] globalVariables = null; private static String[] processed

我将用一些全局变量并行化一些代码。 我将使用ReentrantReadWriteLock。 我是否理解正确,我需要为每个变量提供一个自己的ReentrantReadWriteLock实例,以确保线程安全

我的意思是,当我有两个列表,其中每个线程都可以附加一个项目,而所有线程有时都在从该列表中读取项目。 在这种情况下,我将实现如下内容:

private static String[] globalVariables = null;
private static String[] processedItems = null;

private final ReentrantReadWriteLock globalVariablesLock = new ReentrantReadWriteLock();
private final Lock globalVariablesEeadLock  = globalVariablesLock .readLock();
private final Lock globalVariablesWriteLock = globalVariablesLock .writeLock();
private final ReentrantReadWriteLock processedItemsLock = new ReentrantReadWriteLock();
private final Lock processedItemsLockReadLock  = processedItemsLock .readLock();
private final Lock processedItemsLockWriteLock = processedItemsLock .writeLock();
如果我有更多的变量,如数据库连接(池)、记录器、更多列表等,会怎么样

我是否需要创建一个新的ReentrantReadWriteLock,还是缺少什么? internet上的示例仅处理一个变量


提前感谢。

是的,每个线程安全变量(在您的情况下是数组)应该有一个
锁。但是,考虑使用

ArrayList<String> syncList = Collections.synchronizedList(new ArrayList<String>());
ArrayList syncList=Collections.synchronizedList(新的ArrayList());

而不是数组。当您委托给库时(在这种情况下,不仅同步,而且调整数组的大小),通常会更好。当然,在执行此操作之前,请检查库是否完全符合您的预期(在本例中,正如@SashaSalauyou所指出的,您将失去并发读取的能力)。

解决方案之一是创建一个不可变的
映射,在该映射中,您为所有需要的项设置锁:

final static Map<String, ReadWriteLock> locks = Collections.unmodifiableMap(
     new HashMap<String, ReadWriteLock>() {{
         put("globalVariables", new ReentrantReadWriteLock());
         put("processedItems",  new ReentrantReadWriteLock());
         // rest items
     }}
);

你想保护什么

不要考虑锁定变量。锁的作用是保护不变量。不变量是关于程序状态的断言,它必须始终为真。例如,“变量A、B和C的和总是零。”

在这种情况下,对A、B和C使用单独的锁对您没有任何好处。您需要一个锁来保护特定的不变量。任何想要更改A、B或C的线程都必须锁定该锁,任何依赖于它们的和为零的线程都必须锁定同一个锁

通常情况下,线程不可能在不临时破坏某些不变量的情况下取得进展。例如:

A += 1;    //breaks the invariant
B -= 1;    //fixes it again.
如果没有同步,其他线程可能会检查这两条语句之间的A、B和C,并发现不变量被破坏

使用同步:

private final Object zeroSumLock = new Object();

void bumpA() {
    synchronized(zeroSumLock) {
        A += 1;
        B -= 1;
    }
}

boolean verifySum() {
    synchronized(zeroSumLock) {
        return (A+B+C) == 0;
    }
}

ReadWriteLock
的主要目的是允许并发执行读操作,这比序列化读操作和写操作更有效。使用
synchronizedList
您将失去此功能。@SashaSalauyou嘿,我没有想到这一点!谢谢感谢您直接回答我最初的问题。请在提问时添加语言标签。不是每个人都来自Java世界。哦,我忘了,谢谢你这么做。我不认为这会减少任何东西,此外,我需要生成常量,而不是硬编码文本,以获得正确的锁。我的问题更想问的是,我是否真的需要生成自己的锁,或者我是否误解了这个概念。一个用法是预编译的sql语句列表,其中列表用作缓存。列表是作为一个特定于项目的对象实现的,因此它不仅仅是一个hasmap,我可以将它转换为一个并发等价物。线程应该访问这个列表,获取一个特定的sql,准备它,在上面设置一些变量,并在db上运行它。还有一些线程访问它,却找不到所需的sql。因此,他们在运行时编译它(从另一种特定于项目的类似sql的语言),并将其添加到缓存中。为此,我认为reentrantreadwritelock是一种很好的技术。还有更多类似的对象。@Kaspatoo您刚才描述的绝对是不变的:通过锁定,您试图预存数据完整性,因此当项目具有空间填充属性等时,没有线程可以看到处于状态的列表。@Kaspatoo如果您正在创建自己的缓存类,为什么不在这些类中封装锁?这类任务的常见做法是使用从调用者的角度(例如,
ConcurrentMap\putIfAbsent()
)以原子方式执行的方法组合API,严格封装锁定机制。我无法更改这些类,因为我不是它们的所有者,它们也不会更改它们,因为它们需要更改与其他团队的任何现有接口协议。因此,您对我的问题的回答是使用getter对我的对象进行同步,但使用reentrantreadwritelock可以进行并发读取,但只能进行一次写入,这就是我不打算同步的原因
private final Object zeroSumLock = new Object();

void bumpA() {
    synchronized(zeroSumLock) {
        A += 1;
        B -= 1;
    }
}

boolean verifySum() {
    synchronized(zeroSumLock) {
        return (A+B+C) == 0;
    }
}