Java并发编程规则:设计线程安全的类
2016-12-13 17:20
453 查看
封装设计:
尽管所有的状态都存储在公共静态变量(域)中,仍然能写出线程安全的程序,但比起那些适当封装的类来说,我们难以验证这种程序的线程安全性,也很难再修改它的同步不破坏它的线程安全性。在没有进行全局检查的情况下,封装能够保证线程的安全性。=====设计线程安全类的过程应该包括下面3个基本要素:========
1、确定对象状态是由哪些变量组成的;
2、确定限制状态变量的不变约束;
3、制定一个管理并发访问对象状态的策略。
同步策略方式:
对状态使用同步策略,以维护其不变约束。需要明确的是同步的需求是什么,并分析其不可变约束和后验条件。package net.jcip.examples; import net.jcip.annotations.*; /** * Counter * <p/> * Simple thread-safe counter using the Java monitor pattern * * @author Brian Goetz and Tim Peierls */ @ThreadSafe public final class Counter { @GuardedBy("this") private long value = 0; public synchronized long getValue() { return value; } public synchronized long increment() { if (value == Long.MAX_VALUE) throw new IllegalStateException("counter overflow"); return ++value; } }注:不理解对象的不可变约束和后验条件,就不能保证线程的安全性。要约束状态变量的有效值或者状态转换,就需要原子性与封装性。
实例限制:
限制性使构造线程安全的类更容易。因为类的状态被限制后,分析它的线程安全性时,就不必检查完整的程序。将数据封装在实体内部,把对数据的访问限制在对象的方法上,更易确保线程在访问数据时总能获得正确的锁。示例如下:
package net.jcip.examples; import java.util.*; import net.jcip.annotations.*; /** * PersonSet * <p/> * Using confinement to ensure thread safety * * @author Brian Goetz and Tim Peierls */ @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 { } }HashSet是一个线程安全的类,通过公开的方法进行同步读写操作,确保了线程的安全性。
Java监视器模式:
像Vector和HashTable这样的核心库类,都是采用了Java监视器模式,其最大优势在于简单。Java监视器模式仅仅是一种习惯约定:任意锁对象只要始终如一地使用,都可以用来保护对象的状态。来看个私有锁保护状态的示例:package net.jcip.examples; import net.jcip.annotations.*; /** * PrivateLock * <p/> * Guarding state with a private lock * * @author Brian Goetz and Tim Peierls */ public class PrivateLock { private final Object myLock = new Object(); @GuardedBy("myLock") Widget widget; void someMethod() { synchronized (myLock) { // Access or modify the state of widget } } }实际上同步策略中Counter.java也是一个简单的Java监视器模式的示例。下面是一个基于监视器的机动车追踪器:
package net.jcip.examples; import java.util.*; import net.jcip.annotations.*; /** * MonitorVehicleTracker * <p/> * Monitor-based vehicle tracker implementation * * @author Brian Goetz and Tim Peierls */ @ThreadSafe public class MonitorVehicleTracker { @GuardedBy("this") private final Map<String, MutablePoint> locations; public MonitorVehicleTracker(Map<String, MutablePoint> locations) { this.locations = deepCopy(locations); } public synchronized Map<String, MutablePoint> getLocations() { return deepCopy(locations); } public synchronized MutablePoint getLocation(String id) { MutablePoint loc = locations.get(id); return loc == null ? null : new MutablePoint(loc); } public synchronized void setLocation(String id, int x, int y) { MutablePoint loc = locations.get(id); if (loc == null) throw new IllegalArgumentException("No such ID: " + id); loc.x = x; loc.y = y; } private static Map<String, MutablePoint> deepCopy(Map<String, MutablePoint> m) { Map<String, MutablePoint> result = new HashMap<String, MutablePoint>(); for (String id : m.keySet()) result.put(id, new MutablePoint(m.get(id))); return Collections.unmodifiableMap(result); } } package net.jcip.examples; import net.jcip.annotations.*; /** * MutablePoint * <p/> * Mutable Point class similar to java.awt.Point * * @author Brian Goetz and Tim Peierls */ @NotThreadSafe public class MutablePoint { public int x, y; public MutablePoint() { x = 0; y = 0; } public MutablePoint(MutablePoint p) { this.x = p.x; this.y = p.y; } }
委托线程安全:
委托线程安全,就是使用现有的线程安全的类来实现线程安全,即由其代为控制线程安全。下面来看个使用委托的机动车追踪器,ConcurrentMap线程安全委托:package net.jcip.examples; import java.util.*; import java.util.concurrent.*; import java.awt.*; import java.awt.Point; import net.jcip.annotations.*; /** * DelegatingVehicleTracker * <p/> * Delegating thread safety to a ConcurrentHashMap * * @author Brian Goetz and Tim Peierls */ @ThreadSafe public class DelegatingVehicleTracker { private final ConcurrentMap<String, Point> locations; private final Map<String, Point> unmodifiableMap; public DelegatingVehicleTracker(Map<String, Point> points) { locations = new ConcurrentHashMap<String, Point>(points); unmodifiableMap = Collections.unmodifiableMap(locations); } public Map<String, Point> getLocations() { return unmodifiableMap; } public Point getLocation(String id) { return locations.get(id); } public void setLocation(String id, int x, int y) { if (locations.replace(id, new Point(x, y)) == null) throw new IllegalArgumentException("invalid vehicle name: " + id); } // Alternate version of getLocations (Listing 4.8) public Map<String, Point> getLocationsAsStatic() { return Collections.unmodifiableMap( new HashMap<String, Point>(locations)); } } package net.jcip.examples; import net.jcip.annotations.*; /** * Point * <p/> * Immutable Point class used by DelegatingVehicleTracker * * @author Brian Goetz and Tim Peierls */ @Immutable public class Point { public final int x, y; public Point(int x, int y) { this.x = x; this.y = y; } }Point是不可变对象,它是线程安全的。来看一下将线程安全委托到多个隐含变量:
package net.jcip.examples; import java.awt.event.KeyListener; import java.awt.event.MouseListener; import java.util.*; import java.util.concurrent.*; /** * VisualComponent * <p/> * Delegating thread safety to multiple underlying state variables * * @author Brian Goetz and Tim Peierls */ public class VisualComponent { private final List<KeyListener> keyListeners = new CopyOnWriteArrayList<KeyListener>(); private final List<MouseListener> mouseListeners = new CopyOnWriteArrayList<MouseListener>(); public void addKeyListener(KeyListener listener) { keyListeners.add(listener); } public void addMouseListener(MouseListener listener) { mouseListeners.add(listener); } public void removeKeyListener(KeyListener listener) { keyListeners.remove(listener); } public void removeMouseListener(MouseListener listener) { mouseListeners.remove(listener); } }注:如果一个类由多个彼此独立的线程安全的状态变量组成,并且类的操作不包含任何无效状态转换时,可以将线程安全委托给这些变量。如果一个状态变量是线程安全的,没有任何不变约束限制它的值,并且没有任何状态转换限制它的操作,那么它可以被安全地发布。
安全发布状态:
底层可变的状态也可以是线程安全的类。示例:package net.jcip.examples; import net.jcip.annotations.*; /** * SafePoint * * @author Brian Goetz and Tim Peierls */ @ThreadSafe public class SafePoint { @GuardedBy("this") private int x, y; private SafePoint(int[] a) { this(a[0], a[1]); } public SafePoint(SafePoint p) { this(p.get()); } public SafePoint(int x, int y) { this.set(x, y); } public synchronized int[] get() { return new int[]{x, y}; } public synchronized void set(int x, int y) { this.x = x; this.y = y; } } package net.jcip.examples; import java.util.*; import java.util.concurrent.*; import net.jcip.annotations.*; /** * PublishingVehicleTracker * <p/> * Vehicle tracker that safely publishes underlying state * * @author Brian Goetz and Tim Peierls */ @ThreadSafe public class PublishingVehicleTracker { private final Map<String, SafePoint> locations; private final Map<String, SafePoint> unmodifiableMap; public PublishingVehicleTracker(Map<String, SafePoint> locations) { this.locations = new ConcurrentHashMap<String, SafePoint>(locations); this.unmodifiableMap = Collections.unmodifiableMap(this.locations); } public Map<String, SafePoint> getLocations() { return unmodifiableMap; } public SafePoint getLocation(String id) { return locations.get(id); } public void setLocation(String id, int x, int y) { if (!locations.containsKey(id)) throw new IllegalArgumentException("invalid vehicle name: " + id); locations.get(id).set(x, y); } }
扩展已有线程安全类:
重要思想:缺少即加入。扩展Vector为例:
package net.jcip.examples; import java.util.*; import net.jcip.annotations.*; /** * BetterVector * <p/> * Extending Vector to have a put-if-absent method * * @author Brian Goetz and Tim Peierls */ @ThreadSafe public class BetterVector <E> extends Vector<E> { // When extending a serializable class, you should redefine serialVersionUID static final long serialVersionUID = -3963416950630760754L; public synchronized boolean putIfAbsent(E x) { boolean absent = !contains(x); if (absent) add(x); return absent; } }
使用客户端加锁(缺少即加入):明确可变约束,加锁的位置很重要。
package net.jcip.examples; import java.util.*; import net.jcip.annotations.*; /** * ListHelder * <p/> * Examples of thread-safe and non-thread-safe implementations of * put-if-absent helper methods for List * * @author Brian Goetz and Tim Peierls */ @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) { boolean absent = !list.contains(x); if (absent) list.add(x); return absent; } } }使用组合实现:
package net.jcip.examples; import java.util.*; import net.jcip.annotations.*; /** * ImprovedList * * Implementing put-if-absent using composition * * @author Brian Goetz and Tim Peierls */ @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; } public synchronized void clear() { list.clear(); } // .................. }
相关文章推荐
- Java并发编程4.1-设计线程安全的类
- Java并发编程规则:有状态的线程安全对象在线程池中使用不一定就是线程安全的
- Java并发编程规则:无状态对象永远是线程安全的
- 设计高效的线程安全的缓存(java并发编程实战5.6)
- Java并发编程规则:原子变量实现线程安全
- Java并发编程规则:不可变对象永远是线程安全的
- 探索并发编程(二)------写线程安全的Java代码
- 【Java并发编程】:深入Java内存模型——happen-before规则及其对DCL的分析(含代码)
- 【Java并发编程】之十七:深入Java内存模型—内存操作规则总结
- 【Java并发编程一】线程安全
- 【Java并发编程】之十六:深入Java内存模型——happen-before规则及其对DCL的分析(含代码)
- java并发编程第六章(4)使用基于优先级的阻塞式线程安全列表
- 探索并发编程(二)------写线程安全的Java代码
- JAVA并发设计模式学习笔记(一)—— JAVA多线程编程
- 【Java并发编程】之十六:深入Java内存模型——happen-before规则及其对DCL的分析(含代码)
- JAVA 并发编程-基于线程池设计的ScheduledExecutor(八)
- JAVA 并发编程-基于线程池设计的ScheduledExecutor(八)
- Java高并发编程——为IO密集型应用设计线程数与划分任务
- 【Java并发编程实践】— 线程安全
- Java多线程编程(五)-并发编程原理(写线程安全的Java代码)