《Java并发编程实战》第五章笔记

基础构建模块

在这里插入图片描述

同步容器类

  • 同步容器类包括 Vector 和 Hashtable,二者是早期 JDK 的一部分,此外还包括在JDK 1.2中添加的一些功能相似的类,这些同步的封装器类是由 Collections.synchronizedXxx 等工厂方法创建的。这些类实现线程安全的方式是:将他们的状态封装起来,并对每个公有方法都进行同步,使得每次只有一个线程能访问容器的状态。

同步容器类的问题

在这里插入图片描述

// Vector 上可能导致混乱结果的符合操作

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

在这里插入图片描述

// 在使用客户端加锁的 Vector 上的符合操作

public static Object getLast (Vector list) {
	synchronized (list) {
		int lastIndex = list.size() - 1;
		return list.get(lastIndex);
	}
}

public static void deleteLast (Vector list) {
	synchronized (list) {
		int lastIndex = list.size() - 1;
		list.remove(lastIndex);
	}
}
  • 在调用 size 和相应的 get 之间,Vector 的长度可能会发生变化,这种风险在对 Vector 中的元素进行迭代时仍然会出现。
// 可能抛出ArrayIndexOutOfBoundsException的迭代操作

for (int i = 0; i < vector.size(); i++) {
	doSomething(vector.get(i));
}
// 带有客户端加锁的迭代

synchronized (vector) {
	for (int i = 0; i < vector.size(); i++) {
		doSomething(vector.get(i));
	}
}

迭代器与ConcurrentModificationException

// 通过 Iterator 来迭代 List
List<Widget> widgetList
	= Collections.synchronizedList(new ArrayList<Widget>());
...
// 可能抛出 ConcurrentModificationException
for (widget w : widgetList) {
	doSomething(w);
}	

隐藏迭代器

// 隐藏在字符串连接中的迭代操作

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);
	}
}
  • 正如封装对象的状态有助于维持不变性条件一样,封装对象的同步机制同样有助于确保实施同步策略。

并发容器

  • Java 5.0 提供了多种并发容器类来改进同步容器的性能。同步容器将所有对容器状态的访问都串行化,以实现他们的线程安全性。这种方法的代价是严重降低并发性,当多个线程竞争容器的锁时,吞吐量将严重减低。
  • 通过并发容器来代替同步容器,可以极大地提高伸缩性并降低风险。

ConcurrentHashMap

在这里插入图片描述

额外的原子Map操作

在这里插入图片描述

// ConcurrentMap接口

public interface ConcurrentMap<K, V> extends Map<K, V> {
	// 仅当K没有相应的映射值才插入
	V putIfAbsent (K key, V value);

	// 仅当K被映射到V时才移除
	boolean remove (K key, V value);
	
	// 仅当K被映射到oldValue时才替换为newValue
	boolean replace (K key, V oldValue, V newValue);
	
	// 仅当K被映射到某个值时才替换为 newValue
	V replace (K key, V newValue);
}

CopyOnWriteArrayList

阻塞队列和生产者 - 消费者模式

  • 在构建高可靠的应用程序时,有界队列是一种强大的资源管理工具:他们能抑制并防止产生过多的工作项,使应用程序在负荷过载的情况下变得更加健壮。

示例:桌面搜索

public class FileCrawler implements Runnable {
	private final BlockingQueue<File> fileQueue;
	private final FileFilter fileFilter;
	private final root;
	
	public void run () {
		try {
			crawl(root);
		} catch (InterruptedException e) {
			Thread.currentThread().interrupt();
		}
	}
	
	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 {
	private final BlockingQueue<File> queue;

	public Indexer (BlockingQueue<File> queue) {
		this.queue = queue;
	}
	
	public void run () {
		try {
			while (true) {
				indexFile(queue.take());
			}
		} catch (InterruptedException e) {
			Thread.currentThread().interrupt();
		}
	}
}
// 启动桌面搜索

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();
	}
}

串行线程封闭

双端队列与工作密取

在这里插入图片描述
在这里插入图片描述

阻塞方法与中断方法

在这里插入图片描述
在这里插入图片描述

// 恢复中断状态以避免屏蔽中断

public class TaskRunnable implements Runnable {
	BlockingQueue<Task> queue;
	...
	public void run () {
		try {
			processTask(queue.take());
		} catch (InterruptedException e) {
			// 恢复被中断的状态
			Thread.currentThread().interrupt();
		}
	}
}

同步工具类

  • 在容器类中,阻塞队列是一种独特的类:他们不仅能作为保存对象的容器,还能协调生产者和消费者等线程之间的控制流,因为take和put等方法将阻塞,直到队列达到期望的状态(队列既非空,也非满)。
    同步工具类可以是任何一个对象,只要它根据其自身的状态来协调线程的控制流。阻塞队列可以作为同步工具类,其他类型的同步工具类还包括信号量(Semaphore)、栅栏(Barrier)以及闭锁(Latch)。

闭锁

  • 闭锁是一种同步工具类,可以延迟线程的进度直到其到达终止状态。
// 在计时测试中使用 CountDownLatch 来启动和停止线程

public class TestHarness {
	public long timeTasks (int nThreads, final Runnable task) 
		throws InterruptedException {
		final CountDownLatch startGate = new CountDownLatch(1);
		final CountDownLatch endGate = new CountDownLatch(nThreads);

		for (int i = 0; i < nThreads; i++) {
			Thread t = new Thread() {
				public void run() {
					try {
						startGate.await();
						try {
							task.run();
						} finally {
							endGate.countDown();
						}
					} catch (InterruptedException ignored) { }
				}
			};
			t.start();
		}
		
		long start = System.nanoTime();
		startGate.countDown();
		endGate.await();
		long end = System.nanoTime();
		return end - start;
	}
} 

FutureTask

// 使用 FutureTask 来提前加载稍后需要的数据

public class Preloader {
	private final FutureTask<ProductInfo> future = 
		new FutureTask<ProductInfo>(new Callable<ProductInfo>() {
			public ProductInfo call() throws DataLoadException {
				return loadProductInfo();
			}
		});
	private final Thread thread = new Thread(future);
	
	public void start () { thread.start(); }
	
	public ProductInfo get () 
		throws DataLoadException, InterruptedException {
			try {
				return future.get();
			} catch (ExecutionException e) {
				Throwable cause = e.getCause();
				if (cause instanceof DataLoadException) {
					throw (DataLoadException) cause;
				} else {
					throw launderThrowable(cause);
				}
			}		
	}
}

信号量

在这里插入图片描述

// 使用 Semaphore 为容器设置边界

public class BoundedHashSet<T> {
	private final Set<T> set;
	private final Semaphore sem;
	
	public BoundedHashSet (int bound) {
		this.set = Collections.synchronizedSet(new HashSet<T>());
		sem = new Semaphore(bound);
	}
	
	public boolean add (T o) throws InterruptedException {
		sem.acquire();
		boolean wasAdded = false;
		try {
			wasAdded = set.add(o);
			return wasAdded;
		}
		finally {
			if (!wasAdded) {
				sem.release();
			}
		}
	}
	
	public boolean remove (Object o) {
		boolean wasRemoved = set.remove(o);
		if (wasRemoved) {
			sem.release();
		}
		return wasRemoved;
	}
}

栅栏

// 通过 CyclicBarrier 协调细胞自动衍生系统中的计算

public class CellularAutomata {
	private final Board mainBoard;
	private final CyclicBarrier barrier;
	private final Worker[] workers;
	
	public CellularAutomata (Board board) {
		this.mainBoard = board;
		int count = Runtiome.getRuntime().availableProcessors();
		this.barrier = new CyclicBarrier(count, 
			new Runnable() {
				public void run () {
					mainBoard.commitNewValues();
				}
			});
		this.workers = new Worker[count];
		for (int i = 0; i < count; i++) {
			workers[i] = new Worker(mainBoard.getSubBoard(count, i));
		}
	}
	
	private class Worker implements Runnable {
		private final Board board;
		public Worker (Board board) { this.board = board; }
		public void run () {
			while (!board.hasConverged()) {
				for (int x = 0; x < board.getMaxX(); x++) {
					for (int y = 0; y < board.getMaxY(); y++) {
						board.setNewValue(x, y, computeValue(x, y));
					}
					try {
						barrier.await();
					} catch (InterruptedException ex) {
						return;
					} catch (BrokenBarrierException ex) {
						return;
					}
				}
			}
		}
	}

	public void start () {
		for (int i = 0; i < workers.length; i++) {
			new Thread(workers[i]).start();
		}
		mainBoard.waitForConvergence();
	}
}

构建高效且可伸缩的结果缓存

// 使用HashMap 和 同步机制来初始化缓存

public interface Computable<A, V> {
	V compute (A arg) throws InterruptedException;
}

public class ExpensiveFunction 
	implements Computable<String, BigInteger> {
	public BigInteger compute (String arg) {
		// 在经过长时间的计算后
		return new BigInteger(arg);
	}
}

public class Memoizerl<A, V> implements Computable<A, V> {
	@GuardedBy("this")
	private final Map<A, V> cache = new HashMap<A, V>();
	private final Computable<A, V> c;
	
	public Memoizerl (Computable<A, V> c) {
		this.c = c;
	}
	
	public synchronized V compute (A arg) throws InterruptedException {
		V result = cache.get(arg);
		if (result == null) {
			result = c.compute(arg);
			cache.put(arg, result);
		}
		return result;
	}
}
// 用 ConcurrentHashMap 替换 HashMap

public class Memoizer2<A, V> implements Computable<A, V> {
	private final Map<A, V> cache = new ConcurrentHashMap<A, V>();
	private final Computable<A, V> c;
	
	public Memoizer2 (Computable<A, V> c) { this.c = c; }
	
	public V compute (A arg) throws InterruptedException {
		V result = cache.get(arg);
		if (result == null) {
			result = c.compute(arg);
			cache.put(arg, result);
		}
		return result;
	}
}
// 基于 FutureTask 的 Memoizing 封装器

public class Memoizer3<A, V> implements Computable<A, V> {
	private final Map<A, Future<V>> cache
		= new ConcurrentHashMap<A, Future<V>>();
	private final Computable<A, V> c;
	
	public Memoizer3 (Computable<A, V> c) { this.c = c; } 
	
	public V compute (final A arg) throws InterruptedException {
		Future<V> f = cache.get(arg);
		if (f == null) {
			Callable<V> eval = new Callable<V> {
				public V call () throws InterruptedException {
					return c.compute(arg);
				}
			};
			FutureTask<V> ft = new FutureTask<V>(eval);
			f = ft;
			cache.put(arg, ft);
			ft.run(); // 在这里将调用 c.compute
		}
		try {
			return f.get();
		} catch (ExecutionException e) {
			throw launderThrwable(e.getCause());
		}
	}
}
// Memoizer 的最终实现

public class Memoizer<A, V> implements Computable<A, V> {
	private final ConcurrentMap<A, Future<V>> cache 
			= new ConcurrentHashMap<A, Future<V>> ();
	private final Computable<A, V> c;
	
	public Memoizer (Computable<A, V> c) { this.c = c; }

	public V compute (final A arg) throws InterruptedException {
		while (true) {
			Future<V> f = cache.get(arg);
			if (f == null) {
				Callable<V> eval = new Callable<V>() {
					public V call() throws InterruptedException {
						return c.compute(arg);
					}
				};
				FutureTask<V> ft = new FutureTask<V>(eval);
				f = cache.putIfAbsent(arg, ft);
				if (f == null) { f = ft; ft.run(); }
			}
			try {
				return f.get();
			} catch (CancellationException e) {
				cache.remove(arg, f);
			} catch (ExecutionException e) {
				throw launderThrowable(e.getCause());
			}
		}
	}
}
  • 可变状态是至关重要的。
    • 所有的并发问题都可以归结为如何协调对并发状态的访问。可变状态越少,就越容易确保线程安全性。
  • 尽量将域声明为 final 类型,除非需要他们是可变的。
  • 不可变对象一定是线程安全的。
    • 不可变对象能极大地降低并发编程的复杂性。他们更为简单而且安全,可以任意共享而无须使用加锁或保护性复制等机制。
  • 封装有助于管理复杂性
    • 在编写线程安全的程序时,虽然可以将所有数据都保存在全局变量中,但为什么要这样做?将数据封装在对象中,更易于维持不变性条件:将同步机制封装在对象中,更易于遵循同步策略。
  • 用锁来保护每个可变变量。
  • 当保护同一个不变性条件中的所欲变量时,要使用同一个锁。
  • 在执行复合操作期间,要持有锁。
  • 如果从多个线程中访问同一个可变变量时没有同步机制,那么程序会出现问题。
  • 不要故作聪明地推断出不需要使用同步。
  • 在设计过程中考虑线程安全,或者在文档中明确地指出它不是线程安全的。
  • 将同步策略文档化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值