public class NumberRange {
// INVARIANT: lower <= upper
private final AtomicInteger lower = new AtomicInteger(0);
private final AtomicInteger upper = new AtomicInteger(0);
public void setLower(int i) {
// Warning -- unsafe check-then-act
if (i > upper.get())
throw new IllegalArgumentException(
"can't set lower to " + i + " > upper");
lower.set(i);
}
public void setUpper(int i) {
// Warning -- unsafe check-then-act
if (i < lower.get())
throw new IllegalArgumentException(
"can't set upper to " + i + " < lower");
upper.set(i);
}
public boolean isInRange(int i) {
return (i >= lower.get() && i <= upper.get());
}
}
}
显然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());
System.out.println("DEBUG: added ten elements to " + set);
}
}
能想象这么简单的代码会在什么地方出错么? 答案是最后一行,
System.out.println, 因为用到的set.toString(), 而toString()内部是会用Iterator来访问set的, 一旦此过程中set被修改, 就抛出
ConcurrentModificationException
.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