Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 多线程环境中的对象深度锁定_Java_Multithreading_Concurrency - Fatal编程技术网

Java 多线程环境中的对象深度锁定

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>

我的项目中有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> 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对象时