5

如何同步同一个类的两个不同方法以锁定同一个对象?这是一个例子:

public class MyClass extends Thread implements Observer{
  public List<AnotherClass> myList = null;
  
  public MyClass(List<AnotherClass> myList){
    this.myList = myList;
  }
  
  public void run(){
    while(true){
       //Do some stuff 
       myList.add(NotImportantElement);
    }
  }

  public void doJob{
    for(int i=0; i<myList.size; i++){
      ElementClass x = myList.get(i);
      //Do some more stuff
    }
  }
}

问题是如何在执行 doJob 时阻止 run() 访问 myList,反之亦然?

想象一下:我启动线程并开始将元素添加到我的列表中。在一个随机的时刻,我从另一个持有对我的线程的引用的类中调用 doJob()。

我应该怎么做锁?谢谢!

好的,我理解了锁的概念,但现在我还有一个问题。

假设我有一个类,public static myList并且只有该类的一个实例。从那个实例中,我创建了它n的实例,Thread它获取该列表的每个元素并用它做一些事情。

现在,在特定的时刻,myList被更新。那些已经在处理 myList 元素的线程会发生什么?更新时我应该如何锁定访问权限myList

4

5 回答 5

5

注意:此代码假定您只有一个 MyClass 实例。根据你的帖子,听起来是这样。

public class MyClass extends Thread implements Observer{
  private List<AnotherClass> myList = null;
  private Object lock = new Object();

  public MyClass(List<AnotherClass> myList){
    this.myList = new ArrayList(myList);
  }

  public void run(){
    while(true){
       //Do some stuff 
       synchronized(lock) {
        myList.add(NotImportantElement);
       }
    }
  }

  public void doJob{
    synchronized(lock) {
      for(int i=0; i<myList.size; i++){
        ElementClass x = myList.get(i);
        //Do some more stuff
      }
    }
  }
}

编辑:添加了 List 的副本,以便外部实体无法按照 JB Nizet 更改列表

编辑 2:将变量设为私有,因此其他人无法访问它们

于 2011-10-21T15:28:01.913 回答
5

你可以:

  1. 声明两者rundoJob synchronized。这将this用作锁;
  2. 将列表声明为final并对其进行同步。这将使用列表作为锁。将锁定字段声明final为良好做法。这样,您类的某些方法可以在一个对象上同步,而其他方法可以使用其他对象进行同步。这减少了锁竞争,但增加了代码复杂性;
  3. 引入显式java.util.concurrent.locks.Lock变量并使用它的方法进行同步。这将提高代码的灵活性,但也会增加代码的复杂性;
  4. 不要完全进行显式同步,而是使用 JDK 中的一些线程安全数据结构。例如,BlockingQueueCopyOnWriteArrayList。这将降低代码复杂性并确保线程安全。
  5. volatile通过对字段的读/写来使用同步。请参阅SO 帖子。这将确保安全,但会大大增加复杂性。再三考虑,不要这样做:)
于 2011-10-21T15:31:32.107 回答
1

您可以添加

同步的

两种方法的关键字或使用

synchronized(Myclass.class) {
}

前者本质上使用 Myclass.class 对象,但不如后者细粒度。

于 2011-10-21T15:24:41.670 回答
1

声明这两种方法synchronized来锁定每个实例,或者使用一个synchronized(this){...}块来只对当前实例进行锁定。

于 2011-10-21T15:25:05.330 回答
1
synchronized(myList) {
    // do stuff on myList
}

具体文档:Intrinsic Locks and Synchronization

然而,我鼓励您使用线程安全的并发数据结构来实现您想要实现的目标,以避免自己同步并获得(很多)更好的性能:并发包摘要

于 2011-10-21T15:27:44.793 回答