2011-10-21 113 views
4

我有一个问题给你。如何同步同一个类中的两个不同方法以锁定同一个对象?这里是一个例子:Java同步对象

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 
    } 
    } 
} 

问题是我怎么能停止运行()访问myList时doJob执行和viceversa?

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

我应该怎么做锁?谢谢!

L.E.

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

假设我有一个类public static myList,只有一个该类的实例。在这种情况下,我创建了Threadn实例,它将该列表的每个元素都包含在内,并对其执行一些操作。

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

+0

@Grammin我会说,没关系。 – Mob

+0

如果您还有其他问题,最好的办法是打开另一个帖子。这样就可以看到。 –

回答

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 
     } 
    } 
    } 
} 

编辑:添加制作清单的副本,以便外部实体不能更改列表按JB Nizet

编辑2:制造私有变量,以便其他人可以访问它们

+3

虽然有一个缺陷:由于myList来自外部,所以不能保证另一个线程直接访问myList,绕过锁定。构造函数应该对它收到的列表做一个防御副本。 –

+0

@JB Nizet感谢您指出 - 我修正了样本。也使实例变量私人 –

+0

刚刚“同步”有什么问题?锁定实例,这看起来与少一个对象完全相同。 – DJClayworth

1

您可以添加

同步

关键字这两种方法或使用

synchronized(Myclass.class) { 
} 

前者主要使用Myclass.class对象,但它并不像细随着后者而改变。

1

声明这两种方法为​​来锁定每个实例,或使用synchronized(this){...}块仅在当前实例上创建锁定。

4

您可以:

  1. 同时声明rundoJob​​。这将使用this作为锁;
  2. 声明列表为final并对其进行同步。这将使用列表作为锁。声明锁定字段为final是很好的做法。这样你的类的某些方法可以在一个对象上同步,而其他方法可以使用其他对象进行同步。这减少了锁争用,但增加了代码的复杂性;
  3. 引入明确的java.util.concurrent.locks.Lock变量并使用它的方法进行同步。这将改善代码的灵活性,但也会增加代码的复杂性;
  4. 不要完全执行显式同步,而应该使用JDK中的一些线程安全的数据结构。例如,BlockingQueueCopyOnWriteArrayList。这将减少代码复杂性并确保线程安全。
  5. 通过读取/写入同步到volatile字段。请参阅this SO贴子。这将确保安全,但会大大增加复杂性。关于第二个想法,不要这样做:)
+1

Upvoted for(4.):通过使用并发列表可以避免很多麻烦。 – toto2

+0

当执行运行时,它将被同步,永远不会释放锁 - #1将无法工作。 #2有问题,其他一些对象可以在列表上运行,并且需要同步。 –