多线程摘录 002
??? }
}
对于只有单个需要代理的对象, 可以很方便的应用上述的"代理"方式, 对于多个相互独立的对象而已, 也可以使用
public class VisualComponent {
??? private final List<KeyListener> keyListeners = new CopyOnWriteArrayList<KeyListener>();
??? private final List<MouseListener> mouseListeners = new CopyOnWriteArrayList<MouseListener>();
??? public void add(..);
??? public void remove(...);
}
但是如果这些对象有相互关系, 就出问题了
public class NumberRange { //显然setLower, setUpper, isInRange三个方法的调用没有原子性保障, 而且isInRange同时依赖到两个相关的值, 很容易导致结果错误. 需要增加某些锁定来保证. 或者运行的情况下, 把两个对象合并为一个
public class VectorHelper{public static Object getLast(Vector list) { int lastIndex = list.size() - 1; return list.get(lastIndex);}public static void deleteLast(Vector list) { int lastIndex = list.size() - 1; list.remove(lastIndex);}}非常简单, 很多代码都是这样写的, 但是, 这段代码如果要正常运行, 必须是在假设Vertor对象是不被多个线程共享的情况下. 因为虽然Vector本身是线程安全的, 但VectorHelper不是, 并且getLast和deleteLast同时依赖于Vector.size()来判断最后一个元素. 很容易造成ArrayIndexOutOfBoundsException. 如果Vector被多个线程共享, 最简单的就是加上同步, 然后对一个集合的同步会带来很大的性能代价, 因为阻止了其他线程对集合的访问, 特别是当集合很大并且处理的任务非常大的情况下.
public class HiddenIterator { @GuardedBy("this") private final Set<Integer> set = new HashSet<Integer>(); public synchronized void add(Integer i) { set.add(i); } public synchronized void remove(Integer i) { set.remove(i); } public void addTenThings() { Random r = new Random(); for (int i = 0; i < 10; i++) add(r.nextInt()); }}能想象这么简单的代码会在什么地方出错么? 答案是最后一行, ??? ??? }
public class FileCrawler implements Runnable { //这个是producer角色 private final BlockingQueue<File> fileQueue; private final FileFilter fileFilter; private final File root; ... public FileCrawler(File root, BlockingQueue queue, FileFilter filter){ ... } public void run() { try { crawl(root); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } /*把该目录下所有的文件夹都放进一个queue, 如果满了, 就阻塞*/ private void crawl(File root) throws InterruptedException { File[] entries = root.listFiles(fileFilter); if (entries != null) { for (File entry : entries) if (entry.isDirectory()) crawl(entry); else if (!alreadyIndexed(entry)) fileQueue.put(entry); //把找到的目录放入队列 } }}public class Indexer implements Runnable { //这个是consumer角色 private final BlockingQueue<File> queue; public Indexer(BlockingQueue<File> queue) { // queue在indexer和FileCrawler之间共享 this.queue = queue; } public void run() { try { while (true) indexFile(queue.take()); //从queue中获取一个File对象进行索引 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }}
//这个是客户端的方法, 可以假设roots是在界面设置的几个目录public static void startIndexing(File[] roots) {
BlockingQueue<File> queue = new LinkedBlockingQueue<File>(BOUND); FileFilter filter = new FileFilter() { public boolean accept(File file) { return true; } }; for (File root : roots) new Thread(new FileCrawler(queue, filter, root)).start(); //此处是否可以考虑使用线程池更有效? 而且, 如果有些目录层次非常深, //就会有某些线程运行时间非常长, 相反有些线程非常快就执行完毕. //最恶劣的情况是可能其他线程都完成了, 而退化到只有一个线程中运行, //成为"单线程"程序? for (int i = 0; i < N_CONSUMERS; i++) new Thread(new Indexer(queue)).start(); //此处是否可以考虑使用线程池更有效?}基于上述这种"单线程"的考虑, 不谋而合的, JDK6引入了一种"Work Stealing"的模式, 即N个线程中运行, 每个线程处理自己的事情, 一旦自己手头的事情处理, 那么就去尝试"偷"来其他线程的任务来运行. 这样一来, 系统就会时刻保持多个线程中处理任务, 而不是出现"一人忙活,大家凉快"的情况
转自:http://hi.baidu.com/iwishyou2/blog/item/552e162adab77f305243c116.html