`

观察者模式与多线程

阅读更多

观察者模式是很常用、常见当然也相当重要的一种设计模式,其中主要包括的概念有:

1.Observer(观察者,又具化作ActionListener)

2.Observable(被观察者)

3.Action(观察者所感兴趣的被观察者的行为)

 

被观察者主动与观察者建立契约关系(添加观察者),观察者本身具有行为发生被告知资格,即拥有行为响应方法,如actionPerformed,下面贴出一段程序:

 

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;

// Broken - invokes alien method from synchronized block!
public class ObservableSet<E> extends ForwardingSet<E> {
	public ObservableSet(Set<E> set) {
		super(set);
	}

	private final List<SetObserver<E>> observers = new ArrayList<SetObserver<E>>();

	public void addObserver(SetObserver<E> observer) {
		synchronized (observers) {
			observers.add(observer);
		}
	}

	public boolean removeObserver(SetObserver<E> observer) {
		synchronized (observers) {
			return observers.remove(observer);
		}
	}

	private void notifyElementAdded(E element) {
		synchronized (observers) {
			for (SetObserver<E> observer : observers)
				observer.added(this, element);
		}
	}

	@Override
	public boolean add(E element) {
		boolean added = super.add(element);
		if (added)
			notifyElementAdded(element);
		return added;
	}

	@Override
	public boolean addAll(Collection<? extends E> c) {
		boolean result = false;
		for (E element : c)
			result |= add(element); // calls notifyElementAdded
		return result;
	}
}
 
public interface SetObserver<E> {
	// Invoked when an element is added to the observable set
	void added(ObservableSet<E> set, E element);
}
 
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;

// Reusable forwarding class
public class ForwardingSet<E> implements Set<E> {
	private final Set<E> s;

	public ForwardingSet(Set<E> s) {
		this.s = s;
	}

	public void clear() {
		s.clear();
	}

	public boolean contains(Object o) {
		return s.contains(o);
	}

	public boolean isEmpty() {
		return s.isEmpty();
	}

	public int size() {
		return s.size();
	}

	public Iterator<E> iterator() {
		return s.iterator();
	}

	public boolean add(E e) {
		return s.add(e);
	}

	public boolean remove(Object o) {
		return s.remove(o);
	}

	public boolean containsAll(Collection<?> c) {
		return s.containsAll(c);
	}

	public boolean addAll(Collection<? extends E> c) {
		return s.addAll(c);
	}

	public boolean removeAll(Collection<?> c) {
		return s.removeAll(c);
	}

	public boolean retainAll(Collection<?> c) {
		return s.retainAll(c);
	}

	public Object[] toArray() {
		return s.toArray();
	}

	public <T> T[] toArray(T[] a) {
		return s.toArray(a);
	}

	@Override
	public boolean equals(Object o) {
		return s.equals(o);
	}

	@Override
	public int hashCode() {
		return s.hashCode();
	}

	@Override
	public String toString() {
		return s.toString();
	}
}

 

下面是测试类:

import java.util.HashSet;

public class Test {

	public static void main(String[] args) {
		ObservableSet<Integer> set = new ObservableSet<Integer>(new HashSet<Integer>());
		set.addObserver(new SetObserver<Integer>() {
			public void added(ObservableSet<Integer> s, Integer e) {
				System.out.println(e);
			}
		});
		for (int i = 0; i < 100; i++)
			set.add(i);
	}
}

 如此便有了一个观察者模式的简单示例,但如果往测试类中加一句:

import java.util.HashSet;

public class Test {

	public static void main(String[] args) {
		ObservableSet<Integer> set = new ObservableSet<Integer>(new HashSet<Integer>());
		set.addObserver(new SetObserver<Integer>() {
			public void added(ObservableSet<Integer> s, Integer e) {
				System.out.println(e);
				if (e == 23) s.removeObserver(this);
			}
		});
		for (int i = 0; i < 100; i++)
			set.add(i);
	}
}

 会发现,报以下错误:

Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
    at java.util.AbstractList$Itr.next(AbstractList.java:343)
    at ObservableSet.notifyElementAdded(ObservableSet.java:28)
    at ObservableSet.add(ObservableSet.java:37)
    at Test.main(Test.java:14)

 

The problem is that notifyElementAdded is
in the process of iterating over the observers list when it invokes the observer’s
added method. The added method calls the observable set’s removeObserver
method, which in turn calls observers.remove. Now we are in trouble. We are
trying to remove an element from a list in the midst of iterating over it, which is
illegal. The iteration in the notifyElementAdded method is in a synchronized
block to prevent concurrent modification, but it doesn’t prevent the iterating thread
itself from calling back into the observable set and modifying its observers list.

问题是以下两个方法被同时调用了,当然是同一个线程,所以synchronized关键字对这种行为无效:

public boolean removeObserver(SetObserver<E> observer) {
    synchronized (observers) {
        return observers.remove(observer);
    }
}

private void notifyElementAdded(E element) {
    synchronized (observers) {
        for (SetObserver<E> observer : observers)
	    observer.added(this, element);
    }
}

 问题本质是遍历线程对在处于遍历操作中的List进行remove操作,但改成如下又会造成死锁:

// Observer that uses a background thread needlessly
set.addObserver(new SetObserver<Integer>() {
    public void added(final ObservableSet<Integer> s, Integer e) {
        System.out.println(e);
        if (e == 23) {
            ExecutorService executor = Executors.newSingleThreadExecutor();
            final SetObserver<Integer> observer = this;
            try {
                executor.submit(new Runnable() {
                    public void run() {
                        s.removeObserver(observer);
                    }
                }).get();
            } catch (ExecutionException ex) {
                throw new AssertionError(ex.getCause());
            } catch (InterruptedException ex) {
                throw new AssertionError(ex.getCause());
            } finally {
                executor.shutdown();
            }
        }    
    }
});
 

The background thread
calls s.removeObserver, which attempts to lock observers, but it can’t acquire
the lock, because the main thread already has the lock. All the while, the main
thread is waiting for the background thread to finish removing the observer, which
explains the deadlock.

 

但这样就不会造成死锁:

import java.util.HashSet;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Test {

	public static void main(String[] args) {
		ObservableSet<Integer> set = new ObservableSet<Integer>(new HashSet<Integer>());
		// Observer that uses a background thread needlessly
		set.addObserver(new SetObserver<Integer>() {
			public void added(final ObservableSet<Integer> s, Integer e) {
				System.out.println(e);
				if (e == 23) {
					ExecutorService executor = Executors.newSingleThreadExecutor();
					final SetObserver<Integer> observer = this;
					try {
						executor.submit(new Runnable() {
							public void run() {
								s.removeObserver(observer);
							}
						});
					} finally {
						executor.shutdown();
					}
				}
			}
		});
		
		for (int i = 0; i < 100; i++) {
			set.add(i);
		}
	}
}

 原因应该是backgroud thread在竞争锁时能等待main thread执行完毕(其不阻塞),但也不会打印完整100个数,因为observer会被remove,再调用notifyElementAdded自然也就没有意义,当然原来的方法改变notifyElementAdded也不会造成死锁:

// Alien method moved outside of synchronized block - open calls
private void notifyElementAdded(E element) {
    List<SetObserver<E>> snapshot = null;
    synchronized(observers) {
        snapshot = new ArrayList<SetObserver<E>>(observers);
    }
    for (SetObserver<E> observer : snapshot)
        observer.added(this, element);
}

 还有更好的方法:

// Thread-safe observable set with CopyOnWriteArrayList
private final List<SetObserver<E>> observers =
new CopyOnWriteArrayList<SetObserver<E>>();

public void addObserver(SetObserver<E> observer) {
    observers.add(observer);
}

public boolean removeObserver(SetObserver<E> observer) {
    return observers.remove(observer);
}

private void notifyElementAdded(E element) {
    for (SetObserver<E> observer : observers)
        observer.added(this, element);
}

 In fact, there’s a better way to move the alien method invocations out of the
synchronized block. Since release 1.5, the Java libraries have provided a concurrent
collection (Item 69) known as CopyOnWriteArrayList, which is tailor-made
for this purpose. It is a variant of ArrayList in which all write operations are
implemented by making a fresh copy of the entire underlying array. Because the
internal array is never modified, iteration requires no locking and is very fast. For
most uses, the performance of CopyOnWriteArrayList would be atrocious, but
it’s perfect for observer lists, which are rarely modified and often traversed.

应该是创造了在遍历过程中不会被它线程更改数组结构的机制,而在遍历后或再次遍历时能将写效果实现于读效果之上

分享到:
评论

相关推荐

    Java线程池及观察者模式解决多线程意外死亡重启问题

    Java线程池是Java并发编程中的重要组成部分,它...总之,通过Java线程池和观察者模式的结合,我们可以构建一个健壮的多线程系统,即使在部分线程意外终止的情况下,也能及时发现并采取措施恢复,确保系统的稳定运行。

    Qt设计模式之观察者模式

    在软件开发中,设计模式是一种经过时间和实践验证的解决方案,用于解决常见的编程问题。Qt库,作为一个跨平台的C++图形用户...无论是简单的用户界面交互,还是复杂的多线程数据同步,观察者模式都能提供有力的支持。

    C++观察者模式学习代码

    观察者模式(Observer Pattern)是设计模式中的一种行为模式,它定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并被自动更新。这种模式在软件工程中广泛应用,特别是...

    设计模式-观察者

    7. **其他变体**:除了基本的观察者模式,还有复合观察者模式,允许一个观察者对象包含多个观察者,以及异步观察者模式,通知过程在不同的线程中执行,以避免阻塞主线程。 在实际开发中,理解并熟练运用观察者模式...

    设计模式之观察者模式(Observer)

    在多线程环境下,一个线程完成任务后,可以通过观察者模式通知其他线程进行下一步操作。 观察者模式在Java中得到了很好的支持,`java.util.Observable`和`java.util.Observer`是Java标准库提供的观察者模式实现。...

    多线程(21)观察者模式1

    在多线程编程中,观察者模式常用于监控线程的状态,以便在特定时刻进行相应的操作。下面将详细解释如何使用观察者模式来跟踪和管理线程的生命周期。 首先,我们需要定义一个表示任务生命周期的枚举类型接口`...

    观察者模式 c++ 实现

    观察者模式在C++中的应用非常广泛,例如GUI编程中的事件处理、多线程通信、日志系统等。它提供了一种松耦合的方式,使得主题和观察者之间可以独立地进行扩展和修改,增强了系统的灵活性和可维护性。

    观察者模式Demo

    在实际应用中,观察者模式广泛应用于事件处理、用户界面、多线程通信等领域。例如,Java的`java.util.Observable`和`java.util.Observer`类就是观察者模式的一个实现,用于事件驱动编程。另外,许多现代框架,如...

    Head First 之之观察者模式

    4. **案例分析**:通过实际案例,如用户界面控件的事件处理、多线程环境下的消息传递等,深入分析观察者模式的应用场景和优势,同时讨论可能遇到的问题和解决策略。 5. **扩展与变体**:书籍可能会讨论一些观察者...

    观察者模式代码

    观察者模式,也被称为发布-订阅(Publish-Subscribe)模式,是软件设计模式中的行为模式之一,主要用于定义对象之间的依赖关系。当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。这种模式是...

    Java观察者模式案例

    观察者模式是软件设计模式中的一种行为模式,它在对象之间定义了一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。这种模式广泛应用于事件处理、发布订阅系统、消息...

    委托实现观察者模式

    观察者模式是一种设计模式,它定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的...在实际项目中,如UI界面的事件处理、多线程编程中的回调机制等,都可以看到观察者模式和委托的广泛运用。

    Android ListView 多线程下载 观察者模式更新

    本篇将深入探讨如何利用多线程技术以及观察者模式来解决这个问题。 首先,多线程是Android中处理耗时操作的关键技术。在ListView中,如果每个条目都直接进行网络下载,会占用主线程(UI线程)资源,使得界面无法...

    java23种设计模式之观察者模式

    观察者模式的概念与结构 - **主题(Subject)**:它是被观察的对象,通常包含两个接口,一是`addObserver()`用于添加观察者,二是`removeObserver()`用于移除观察者。同时,它还有一个`notifyObservers()`方法,...

    Java 多线程编程中的设计模式~

    2. **观察者模式**:在多线程环境中,观察者模式常用于实现线程间的通信,其中一个对象(主题)的状态改变时会通知所有依赖于它的观察者对象,使得它们能够自动更新自己的状态。 3. **职责链模式**:这种模式可以...

    设计模式 中的 单例模式和观察者模式

    观察者模式是一种行为设计模式,它定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。这种模式广泛应用于事件处理、发布/订阅系统等。在Java中,可以使用`...

    基于观察者模式两个activity通信

    在这个场景中,我们关注的是如何使用观察者模式(Observer Pattern)来实现Activity A和C之间的数据交互。观察者模式是一种行为设计模式,它允许一个对象(观察者)在另一个对象(被观察者)的状态改变时自动收到...

    观察者模式

    5. **应用场景**:观察者模式广泛应用于各种领域,如用户界面的事件处理、多线程同步、消息队列等。例如,在GUI编程中,当用户点击按钮时,按钮对象作为主题,可以通知所有关联的观察者(如窗口、菜单等)进行相应...

    [行为模式]head first 设计模式之观察者模式(observer)

    《观察者模式——深入解析与应用》 观察者模式(Observer Pattern)是设计模式中的一种行为模式,它定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。这种...

    java 子线程通过观察者模式通知主线程

    在多线程环境下,观察者模式可以被巧妙地应用于子线程通知主线程的情况。 观察者模式的核心思想是定义一个一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。在Java中,`...

Global site tag (gtag.js) - Google Analytics