Java 多线程环境中的对象深度锁定
我的项目中有A类和B类的以下结构:Java 多线程环境中的对象深度锁定,java,multithreading,concurrency,Java,Multithreading,Concurrency,我的项目中有A类和B类的以下结构: class A { private String id; private List<B> bs = new ArrayList<B>(); A(String id){ this.id = id; } public List<B> getBs(){ return bs; } public void setBs(List<B>
class A {
private String id;
private List<B> bs = new ArrayList<B>();
A(String id){
this.id = id;
}
public List<B> getBs(){
return bs;
}
public void setBs(List<B> bs){
this.bs=bs;
}
public void addBs(List<B> bs){
this.bs.addAll(bs);
}
public void addB(B b){
this.bs.add(b);
}
}
class B {
private String id;
private List<String> tags;
B(String id){
this.id = id;
}
public List<String> getTags(){
return tags;
}
public void setTags(List<String> tags){
this.tags = tags;
}
public void addTags(List<String> tags){
this.tags.addAll(tags);
}
public void addTag(String tag){
this.tags.add(tag);
}
}
a1.add(b1);
a1.add(b2);
a2.add(b3);
a2.add(b4);
同时,我将B类对象放入列表中对象内a1
和a2
请务必注意,唯一的B对象在任何a对象中只放置一次:
class A {
private String id;
private List<B> bs = new ArrayList<B>();
A(String id){
this.id = id;
}
public List<B> getBs(){
return bs;
}
public void setBs(List<B> bs){
this.bs=bs;
}
public void addBs(List<B> bs){
this.bs.addAll(bs);
}
public void addB(B b){
this.bs.add(b);
}
}
class B {
private String id;
private List<String> tags;
B(String id){
this.id = id;
}
public List<String> getTags(){
return tags;
}
public void setTags(List<String> tags){
this.tags = tags;
}
public void addTags(List<String> tags){
this.tags.addAll(tags);
}
public void addTag(String tag){
this.tags.add(tag);
}
}
a1.add(b1);
a1.add(b2);
a2.add(b3);
a2.add(b4);
这样,我们可以在缓存中为类A和B创建多个对象
场景:现在多个线程访问此缓存,但根据用户指定的ID,其中一些线程最终会获取A类对象,而另一些线程会获取B类对象。这些线程实际上希望读取这些对象的信息或更新这些对象的信息
问题:我的要求是,当一个线程访问a类对象(例如a1
)来更新它时,其他线程就不能读取或更新a1
以及所有B类对象(b1
和b2
)它们被添加到列表
内部对象a1
,直到我完成a1
上的所有更新。请告诉我在这种情况下如何获得锁 关键字synchronized
将帮助您完成此操作。您可以将整个方法声明为synchronized
,也可以将synchronized()
块锁定在您定义的特定密钥对象上。输入synchronized()
块时,在该块退出之前,其他线程无法访问使用同一密钥对象锁定的其他块
请参阅关于同步的Java教程
例如,您可以执行以下任一操作:
public synchronized void addB(B b) {
this.bs.add(b);
}
或
。。。在类中声明锁对象
private final Object LOCK = new Object();
。。。并将其用作synchronized()
块:
public void addB(B b) {
synchronized(LOCK) {
this.bs.add(b);
}
}
与第一种方法相比,使用第二种方法的优点是,您可以完全、显式地控制哪些代码段被锁定(而不仅仅是整个方法调用)。在处理并发时,为了提高效率,您希望尽可能少地进行同步,因此使用此方法,您可以只执行最小限度的同步
另外,我相信有人会指出,您不需要显式声明另一个锁对象,因为您可以使用this
关键字在当前对象上进行同步。但是,总结一下我不建议这么做的原因。要进行深度复制,我建议在a和B的每个实例中都使用a,并让它们都使用lock()
和unlock()
方法实现一些接口,其中类a
将获得自己的锁,以及B的所有锁。然后,在使用前锁定对象,并在完成后解锁
编辑:因此您的行动方针是:
创建一个接口,我们用两种方法将其称为Lockable
,lock()
和unlock()
让A和B都实现该接口。因此,您的缓存现在将使用可锁定
而不是对象
将专用字段添加到a和B
私有最终锁=新的()
现在,B中的Lockable
的实现将只是在锁上调用相同的方法
在A中,lock()
将获取lock的本地实例,并迭代b的列表并调用它们的lock()
方法。同样适用于unlock()
现在,每次从缓存中获取对象时,在对其执行任何操作之前,都要对该对象调用lock()
,然后在完成后调用unlock()
EDIT2:我本打算在问题编辑后完成我的回答,但我刚刚找到了类,我认为它完全符合用户sohanlal的需要
此类提供了一对锁,其中一个用于读取操作,这些操作可以同时由多个元素拥有,并且不会被自身阻塞。第二个(写入部分)在获取时避免任何读取或写入操作
解决方案如下所示:
public synchronized void addB(B b) {
this.bs.add(b);
}
将锁添加到容器类A。此类是读写锁的真正所有者
将lock属性添加到类B。此类不拥有锁,但仅引用它以读取锁
将新B对象添加到a容器时,向B对象提供对容器上ReentrantReadWriteLock的引用。从a中提取B对象时,取消引用从容器“继承”的锁
在类A中,对修改B对象集合(添加、移除…)的每个操作写入锁。可选地(如果B元素的基础集合未同步),读取不修改集合但访问它的锁定操作
在B类中,读取每个操作上的锁定
代码。这尚未经过测试,但将给出如何实施的想法:
class A {
private String id;
private List<B> bs = new ArrayList<B>();
private ReentrantReadWriteLock lk = new ReentrantReadWriteLock();
A(String id){
this.id = id;
}
public List<B> getBs(){
lk.readLock().lock(); // acquire the read lock, since this operation does not affect A contents
return bs;
lk.readLock().unlock();
}
public void setBs(List<B> bs){
lk.writeLock().lock(); // acquire the write lock, which automatically avoids further reading/writing operations
this.bs=bs;
for( B elem : bs )
{
// internal B elements need a reference to the reading part of the lock
elem.setLock(lk.readLock());
}
lk.writeLock().unlock();
}
public void addBs(List<B> bs){
[...] // similar approach that in setBs
}
public void addB(B b){
[...] // similar approach that in setBs
}
public void deleteB( B elem ) // Or whatever notation you want
{
lk.writeLock().lock(); // acquire the write lock
B internalElem = bs.get(bs.indexOf(elem));
if( internalElem != null )
{
bs.remove(internalElem);
bs.unsetLock();
}
lk.writeLock().unlock();
}
}
class B {
private String id;
private List<String> tags;
private Lock lk;
B(String id){
this.id = id;
lk = null;
}
public void setLock(Lock l){ lk = l; } // put additional controls if you want
public void unsetLock()
{
lk = null;
}
private void lockInternal()
{
if(lk!=null){ lk.lock(); }
}
private void unlockInternal()
{
if(lk!=null){ lk.unlock(); }
}
public List<String> getTags(){
List<String> ref = null;
lockInternal();
[...] //internal operations
unlockInternal();
return ref;
}
public void setTags(List<String> tags){
[...] // similar approach that in getTags
}
public void addTags(List<String> tags){
[...] // similar approach that in getTags
}
public void addTag(String tag){
[...] // similar approach that in getTags
}
}
问题是,如果您在a1
上操作,您希望锁定b1
、b2
和b3
上的任何操作。你怎么能确定呢
我看到的唯一解决方案是A容器中的所有B元素共享一些公共锁变量。在调用a1
上的写相关操作时,它获取锁,避免对B元素进行任何操作。请注意,对B元素的任何操作都不需要实际获取锁。。。仅检查是否已由A-type容器获取
许多缺点/考虑因素:
- 性能:对B对象的每个操作都需要检查锁(这可能非常昂贵)
- 操作约束:如果一个B对象可以添加到多个a类型容器中,这将变得更加复杂
- 实现复杂度:在中插入B对象时