第四章 对象的组合
4.1 构建安全的类
4.2 实例封闭
@ThreadSafe public class PersonSet { @GuardedBy("this") private final Set<Person> mySet = new HashSet<Person>(); public synchronized void addPerson(Person p) { mySet.add(p); } public synchronized boolean containsPerson(Person p) { return mySet.contains(p); } interface Person { } }
对personset的对象myset进行了封装,但是person的安全性未知,也叫java监视器模式
使用私有锁来保护对象
public class PrivateLock { private final Object myLock = new Object(); @GuardedBy("myLock") Widget widget; void someMethod() { synchronized (myLock) { // Access or modify the state of widget } } }
4.3 线程安全性的委托
不可变的域是线程安全的,可以被自由的共享与发布
应用线程安全的容器来封装类中的状态域来实现线程安全,将类的安全***给底层的其他类去实现,就叫做线程安全性委托,但是如果存在多个相互依赖的组合对象时,还必须用锁来实现安全性。
4.4 在现有的线程安全类中添加功能
1)在原始类中添加方法
2)通过继承来拓展原来的类
3)通过一个辅助类来添加功能
客户端加锁:
@NotThreadSafe class BadListHelper <E> { public List<E> list = Collections.synchronizedList(new ArrayList<E>()); public synchronized boolean putIfAbsent(E x) { //内置锁对于客户端来讲是不同的 boolean absent = !list.contains(x); if (absent) list.add(x); return absent; } } @ThreadSafe class GoodListHelper <E> { public List<E> list = Collections.synchronizedList(new ArrayList<E>()); public boolean putIfAbsent(E x) { synchronized (list) { //list对象锁对每个客户端来讲都是同一个 boolean absent = !list.contains(x); if (absent) list.add(x); return absent; } } }
上述第一个内置类是谁调用就是谁
组合:
@ThreadSafe public class ImprovedList<T> implements List<T> { private final List<T> list; /** * PRE: list argument is thread-safe. */ public ImprovedList(List<T> list) { this.list = list; } public synchronized boolean putIfAbsent(T x) { boolean contains = list.contains(x); if (contains) list.add(x); return !contains; }
将list引入作为自己的成员变量,此时的内置锁就是该类的对象本身
第五章 基础构建模块
5.1 同步容器类:Vector Hashtable
通过封装,对每个公有方法都进行同步,使得每次只能有一个线程来访问容器的状态,将所有对容器状态的访问都串行化,效率很低。
同步容器类都是线程安全的,但是需要自己加锁来执行复杂的复合操作,比如迭代等。迭代过程中,如果有其他线程在修改该容器,则会出现错误。设计同步类容器时并没有解决该问题,因此在使用中还是需要自己加锁来避免异常。但是这会使得效率很低,因为整个迭代期间其他线程不得访问,改进办法为容器克隆。
提防另外一种隐藏迭代:“helloworld”+set;将调用set的toString()函数,在该函数里面执行迭代,容器的hashcode和equals方法也会执行迭代。
5.2 并发容器:concurrentHashMap CopeOnWriteArrayList
concurrentHashMap:并不是将每个方法都放在同一个锁上同步并且同时只能有一个线程访问容器,二十使用了更细粒度的分段锁来实现更大程度的共享。其提供的迭代器不会抛出异常,因此不需要在迭代过程中加锁。
CopeOnWriteArrayList:用于替代同步list,迭代期间不需要加锁和复制。每次修改时都会复制底层数组,修改结束后重新发布一个新的容器版本。
5.3 阻塞队列和生产者-消费者模式
blockingQueue
5.4 阻塞方法与中断方法
当在代码中调用了一个将抛出interruptedException异常的方法时,自己就变成了一个可阻塞方法,在捕获到interruptedException异常时,最明智的选择是传递给调用者,即再次抛出interruptedException异常。如果不能抛出异常,则调用该线程的interrupt方法恢复中断,上层代码将看到引发了一个中断。比如:
public class TaskRunnable implements Runnable { BlockingQueue<Task> queue; public void run() { try { processTask(queue.take()); } catch (InterruptedException e) { // restore interrupted status Thread.currentThread().interrupt(); } } void processTask(Task task) { // Handle the task } interface Task { } }
5.5 同步工具类
闭锁:countDownLatch
futureTask:异步事件
信号量:counting semaphore
栅栏:
闭锁用于等待事件,栅栏用于等待进程。栅栏可重置用于重复使用