`
coolxing
  • 浏览: 874159 次
  • 性别: Icon_minigender_1
  • 来自: 北京
博客专栏
9a45b66b-c585-3a35-8680-2e466b75e3f8
Java Concurre...
浏览量:97276
社区版块
存档分类
最新评论

利用对象限制和委托构建线程安全的类--Java Concurrency In Practice C04读书笔记

阅读更多

[本文是我对Java Concurrency In Practice第三章的归纳和总结.  转载请注明作者和出处,  如有谬误, 欢迎在评论中指正. ] 

设计线程安全的类需要考虑:

1. 确定组成对象状态的变量.

2. 确定约束对象状态的不变式.

3. 建立并发访问对象状态的规则.

 

后置条件: 由于某些变量的取值是有限制范围的, 改变状态变量之后需要检查改变后的状态是否合法. 后置条件要求进行额外的同步. 比如一个计数器, 当前的值为17, 那么进行一次操作之后其取值只能是18, 其他值都是非法的.

前置条件: 需要满足一定的条件操作才能继续进行. 多线程环境下, 可以使用内置的wait和notify机制, 在条件满足时通知另一个线程继续往下执行. 也可以使用阻塞队列, 信号量等实现. 

 

对象限制

借助封装, 可以将对象限制在类范围内(作为类的私有成员), 方法内部(作为方法的局部变量), 或者线程范围内(只能在特定线程中访问该对象).

将对象限制在类范围内的例子:

public class PersonSet { 
    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); 
    } 
} 

mySet对象不是线程安全的, 但是将mySet对象限制在PersonSet类中, 并通过synchronized同步所有访问mySet对象的方法, 而且mySet对象没有逃逸出PersonSet类范围, PersonSet就是一个线程安全的类.

由上面的例子可知, 受限的对象不能逃逸出所在的范围是非常重要的.

另一个例子是java.util.Collections类提供的一些静态方法, 比如Collections.synchronizedList(List<T> list)方法:

class SynchronizedList<E> extends SynchronizedCollection<E> implements List<E> {
	static final long serialVersionUID = -7754090372962971524L;

	final List<E> list;

	SynchronizedList(List<E> list) {
		super(list);
		this.list = list;
	}

	SynchronizedList(List<E> list, Object mutex) {
		super(list, mutex);
		this.list = list;
	}

	public boolean equals(Object o) {
		synchronized (mutex) {
			return list.equals(o);
		}
	}

	public int hashCode() {
		synchronized (mutex) {
			return list.hashCode();
		}
	}

	public E get(int index) {
		synchronized (mutex) {
			return list.get(index);
		}
	}
	// ....
}

可以看出SynchronizedList是对List的包装. SynchronizedList所有的方法都使用同一把锁mutex进行同步. 所以SynchronizedList是一个线程安全的类. 但是前提是被包装的list对象不能再直接使用, 必须通过synchronizedList(List<T> list)返回的对象进行访问(开发者必须遵循这样的约定, 否则线程安全就被破坏了), 也就是防止list对象逃逸出SynchronizedList类的范围.

 

委托线程安全. 先看一个例子:

public class DelegatingVehicleTracker {
    private final ConcurrentMap<String, Point> locations; 

    public DelegatingVehicleTracker(Map<String, Point> points) { 
	// 构造map的concurrent视图
        locations = new ConcurrentHashMap<String, Point>(points); 
    } 

    public Map<String, Point> getLocations() { 
    	// 返回map的unmodifiable视图
        return Collections.unmodifiableMap(locations); 
    } 

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

    /**
     * 这是一个不可变类
     */
    public class Point { 
        public final int x, y; 
        public Point(int x, int y) { 
            this.x = x; 
            this.y = y; 
        } 
    }
}

DelegatingVehicleTracker只有一个状态变量: locations. locations是一个ConcurrentMap对象, 也就是说locations是线程安全的. 而且locations对象被限制在DelegatingVehicleTracker类范围内. 因此可以说, DelegatingVehicleTracker是一个线程安全的类因为locations是线程安全的. 换一种说法, DelegatingVehicleTracker将线程安全的责任委托给了locations对象. 

在构造函数里, 利用points的concurrent视图初始化locations. concurrent视图不同于synchronized视图, synchronized视图只是包装了一层同步, 方法的调用还有由原有的Map对象负责的, 因此JDK文档特别要求不能在外部继续调用原有Map的方法, 所以线程安全来源于约定, 如果开发者没有遵循约定, 线程安全就被破坏了. 但是concurrent视图并非如此, concurrent视图是对原有Map的浅拷贝, 也就是会遍历原有Map, 取出其中的每一个键值对, 然后将取出的键值对存入concurrent视图中. 因此concurrent视图仅和原有的Map共享了键和值的引用, 而由于Map的value(Point对象)是不可变的, 在外部直接调用原有的Map的任何方法, 都不会对concurrent视图造成影响.

我们可以总结出这样的规则: 如果类只包含一个状态变量, 那么类是否是线程安全的, 取决于该状态变量是否是线程安全的.

如果类中存在多个状态变量时, 委托规则是否仍然成立呢? 如下:

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

CopyOnWriteArrayList是ArrayList的线程安全的变体, keyListeners和mouseListeners是线程安全的对象, 而且keyListeners和mouseListeners是相互独立的, 所以VisualComponent也是线程安全的. 当状态变量之间不是相互独立的时候情况又如何呢? 如:

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

NumberRange类的状态变量lower和upper都是线程安全的对象, 但是lower和upper不是相互独立的, 两者需要满足不变式约束: lower <= upper. 假设lower和upper分别是0和10, 此时2个不同的线程同时调用setLower(5)和setUpper(4), 在某些不幸的时间点这2个方法的前置条件检查都能通过, 这就造成不变式约束不再成立. 因此NumberRange类不是线程安全的.

总结: 如果类中的状态变量之间是相互独立的, 那么线程安全的责任就可以委托给状态变量: 类是否线程安全, 取决于所有状态变量是否线程安全.

 

2
1
分享到:
评论

相关推荐

    Java并发编程实践(java concurrency in practice)pdf (java多线程总结.ppt)

    8. **原子变量**:`java.util.concurrent.atomic`包中的原子变量类如`AtomicInteger`和`AtomicReference`,提供了无锁编程的支持,可以在不使用同步的情况下实现线程安全。 9. **并发设计模式**:书中涵盖了如生产...

    Java Concurrency in Practice 无水印pdf

    Java Concurrency in Practice 英文无水印pdf pdf所有页面使用FoxitReader和PDF-XChangeViewer测试都可以打开 本资源转载自网络,如有侵权,请联系上传者或csdn删除 本资源转载自网络,如有侵权,请联系上传者...

    Java Concurrency in Practice.zip

    《Java Concurrency in Practice》是Java并发编程领域的一本经典著作,由Brian Goetz、Tim Peierls、Joshua Bloch、Joseph Bowles和Doug Lea等专家共同编写。这本书深入探讨了Java平台上的多线程和并发编程,旨在...

    java并行编程(Java Concurrency in Practice) 英文版chm

    &lt;&lt;java并行编程&gt;&gt;英文版chm格式,英文名称&lt;Java Concurrency in Practice&gt;,一直想买这本书,但总是缺货,找到了电子版,分享给大家。 Java Concurrency in Practice By Brian Goetz, Tim Peierls, Joshua Bloch,...

    Java Concurrency in Practice

    Java Concurrency in practice

    Java Concurrency In Practice.pdf

    在并发编程中,可以通过委托机制让一个线程安全的对象负责处理另一个非线程安全对象的操作。书中详细解释了如何利用这种方式来增强现有类的线程安全性。 ##### 4.4 向现有线程安全类添加功能 在不破坏原有线程安全...

    Java Concurrency in Practice JAVA并发编程实践(中英文版)

    Using the concurrency building blocks in java.util.concurrent Performance optimization dos and don'ts Testing concurrent programs Advanced topics such as atomic variables, nonblocking algorithms, ...

    java_concurrency_in_practice_source源代码

    这里的"java_concurrency_in_practice_source"源代码正是书中实例的实现,它涵盖了Java多线程编程中的关键概念和技术。 1. **线程基础**:Java中创建线程有两种方式,一是通过`Thread`类的子类,二是实现`Runnable`...

    Java Concurrency In Practice Learning Note

    总的来说,《Java Concurrency In Practice》和Guava库为我们提供了丰富的理论知识和实践经验,通过深入学习和实践,我们可以构建出高效、可靠的并发应用程序。在实际工作中,我们需要不断探索、理解和运用这些工具...

    Java Concurrency in Practice JAVA并发编程实践中文版(全)

    Java Concurrency in Practice JAVA并发编程实践中文版(全)第二部分

    java concurrency in practice

    java concurrency in practice 经典的多线程编程书籍,英文版

    Concurrent_Programming+Java Concurrency in Practice+langspec

    首先,"Java Concurrency in Practice"是Java并发编程的经典之作,由Brian Goetz、Tim Peierls、Joshua Bloch、David Holmes和Doug Lea合著。这本书提供了一套实用的指导原则、设计模式和最佳实践,帮助Java开发者...

    Java并发实践英文版(Java Concurrency in Practice)

    - **书名**:《Java并发实践》(Java Concurrency in Practice) - **作者**:Brian Goetz, Tim Peierls, Joshua Bloch, Joseph Bowbeer, David Holmes, Doug Lea - **出版社**:Addison Wesley Professional - **...

    Java Concurrency in Practice电子书PDF加源码包

    java_concurrency_in_practice.pdf jcip-examples-src.jar jcip-annotations-src.jar 英文版是高清晰的,实战和实践都是同一帮人对英文版书的翻译,网传实战的翻译质量更好,实战是2012年出版的,应该是对前一版实践...

Global site tag (gtag.js) - Google Analytics