`

观察者模式与多线程

阅读更多

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

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线程池及观察者模式解决多线程意外死亡重启问题,附件含两个要运行代码!

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

    多线程下载,包括暂停、继续下载、取消; 使用观察者模式进行针对更新,防止ListView下载进度乱串

    用观察者模式和非观察者模式设计一个交通信号灯调度系统

    用观察者模式设计一个交通信号灯调度系统,有需要的朋友可以参考下。这是本人写一篇有关于Java中设计模式,多线程调度的一个Demo吧,本人水平有限,望各位博友批评指正。此Demo实现了现实交通信号灯调度车辆通行的功能,...

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

    背景:当线程在运行的时候,如果没有使用线程池等包的时候,那么直接newThread这种方法的话,是很难观察到线程目前的状态的,如果知道当前这个线程是运行还是停止

    Android基于观察者模式下载管理

    Android 基于观察者模式下载管理 断点续传 ,多线程管理 ,listView item,RecycleView item 刷新

    Java设计模式—观察者模式详解

    主要介绍了Java设计模式—观察者模式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

    android 网络应用轻量框架-多线程管理-高效缓存-设计模式

    6:使用状态模式 观察者模式更好的处理多线程 最初的想法:网络优化开发框架 (移除任务未完成) 网络稳定,系统运行稳定性,大内存消耗稳定,长时间运行稳定性 (旧的系统症结所在) 开启过多线程,导致系统...

    java设计模式

    22.4.1 Java世界中的观察者模式 22.4.2 项目中真实观察者模式 22.4.3 订阅发布模型 22.5 最佳实践 第23章 门面模式 23.1 我要投递信件 23.2 门面模式的定义 23.3 门面模式的应用 23.3.1 门面模式的优点 23.3.2 门面...

    把多个任务分派给多个线程去执行

    由三个类实现,写在了一个 Java 文件中:...代码中运用了命令模式,如若能配以监听器,用上观察者模式来控制 UI 显示就更绝妙不过了,就能实现像下载中的区块着色跳跃的动感了,在此定义下一步的着眼点了。

    java:多线程+MVC实现的贪吃蛇

    没使用观察者模式,而是尝试启动多个线程来完成帧更新,数据读取和数据显示 水平有限,多多包涵

    设计模式,软件开发者必读

    4.6 OBSERVER 观察者模式 83 4.7 MEMENTO 备忘录模式 87 4.8 STATE 状态模式 91 4.9 STRATEGY 策略模式 96 4.10 TEMPLATE METHOD模板方法 100 4.11 VISITOR 访问者模式 103 C++高级编程 108 5.1 多态及其实现机制 ...

    J2SE笔记讲解个人修订(1.1).docx

    9 观察者模式 10 内部类讲解 11 JAVA I/O流讲解 12 JAVA多线程 13 JAVA网络通信 14 JAVA类加载器CLASSLOADER 15 JAVA简单工厂模式 16 JAVA中的注解 17 JAVA 图形界面 18 JAVA多线程 19 JAVA 反射机制 20 ...

    java高级+struts2总结

    IO流高级 设计模式 多线程 网络编程 struts2 注解 反射 MVC+Log4 资源文件 泛型 观察者模式 装饰模式

    深入浅出设计模式(中文版)

    5.7ObserverPattern(观察者模式) 236 5.7.1定义 236 5.7.2现实例子——拉登现身了 238 5.7.3C#实例——猫和老鼠 238 5.7.4C#实例——股票变化 241 5.7.5Java实例——监控系统 245 5.7.6优势和缺陷 248 ...

    覆盖一系列高级主题,包括复杂的语法和特性、Python的高级编程技巧、常见的设计模式、并发编程、性能优化等

    设计模式: 探讨常见的设计模式,如工厂模式、单例模式、观察者模式等,以及如何在Python中应用这些模式。 测试和调试: 介绍高级的测试技术和调试工具,以确保代码的质量和可维护性。 性能优化: 提供关于Python...

    JAVA超全面试突击包-答案讲义

    涵含各种重要的JAVA编程知识点,面试突击专用 内容包括:Spring、SprngBoot、SpringCloud、Redis、MySQL、MyBatis、JVM、多...设计模式:包括常见的设计模式,如单例模式,工厂模式,观察者模式等,以及它们的使用场景

    深入浅出设计模式(中文版电子版)

    5.7ObserverPattern(观察者模式) 236 5.7.1定义 236 5.7.2现实例子——拉登现身了 238 5.7.3C#实例——猫和老鼠 238 5.7.4C#实例——股票变化 241 5.7.5Java实例——监控系统 245 5.7.6优势和缺陷 248 ...

    Android-MultiDownloader_as:Android框架来处理多个线程的并发

    观察者模式当您有一些对象必须将一个对象的状态更改通知所有感兴趣的对象时,将使用观察者模式。 在图形上,这是这样工作的: 下图是它的uml表示:让我们建立模型观察者接口可以使用以下结构构建: public interface...

    北京百度java面试题大全

    Java面试题是针对Java编程语言的技术和知识的一系列问题,用于考察面试者在Java开发方面的能力和...设计模式:涉及常见的设计模式,如单例模式、工厂模式、观察者模式等。 Java框架和技术:包括Spring、Hibernate、My

Global site tag (gtag.js) - Google Analytics