java并发编程实战------阅读笔记第五章基础构建模块

本文探讨了同步容器类如Vector和Hashtable的局限性及并发操作时可能出现的问题,并介绍了并发容器如ConcurrentHashMap的优势。此外,还讨论了阻塞队列、阻塞方法、同步工具类等在多线程编程中的应用。

一、同步容器类 1、早期的同步容器类Vector ,Hashtable,类似于Collections.synchronizedXxxx方法创建的,就是用装饰器模式把线程不安全的容器类包装起来,所有公共方法都进行同步,这种容器性能较低。

2、同步容器来的问题(复合操作依然会导致混乱的结果) Vector是线程安全的,只是说明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);
	}

线程A执行第一步完,尚未执行第二步;线程B执行了第一步,再执行第二步完;之后线程A再执行第二步,这个时序就会出现问题集合元素个数变了而线程A不知道,会跑出数组越界异常,因为这些操作不是原子性。通过给被操作容器类加锁可以把这些复合操作变成原子操作(但其实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);
		}

迭代操作也会出现多线程操作导致混乱的问题,在迭代操作上加锁保护起来解决问题,但是降低了并发性。

//可能会被多线程操作而混乱结果,因为迭代是复合操作,不是原子操作		
for(int i = 0;i < vector.size();i++){
			//TODO
		}
//加锁锁住这个vector即可,但是降低了并发性,其他线程不能操作vector
synchronized(vector){
			for(int i = 0;i < vector.size();i++){
			//TODO
			}
		}

迭代器与ConcurrentModificationException:即使是同步容器类也没有考虑并发修改问题,对应策略是'fail-fast'快速失败,当发现容器被修改时就抛出异常。改善这种情况可以在迭代时对容器进行克隆,迭代副本。 给容器加锁来进行迭代涉及面太广了,不合适,而且有些迭代器操作是隐藏的,容易被忽视。

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

}

容器的hashCode和equals方法,将容器整体放入其他容器,containsAll、removeAll、retainAll等等方法,以及把容器作为参数的构造函数,都会隐含对容器进行迭代,这些迭代都有可能抛出ConcurrentModificationException。

二、并发容器 1、ConcurrentHashMap,使用了分段锁,可以支持任意数量的读线程操作Map;支持多个读线程和写入线程操作Map;可以支持一定数量写入线程同时操作Map,迭代操作不会加锁,采用了弱一致性而不是快速失败,提高了并发性和吞吐量。并发Map还提供了一些复合原子操作,比如存在则删除,存在则替换,不存在则插入等等。 2、CopyOnWriteList,CopyOnWriteHashSet:支持并发操作,迭代不会加锁。

三、阻塞队列和生产者消费者模型 生产者消费者模型的阻塞队列实现了可变对象的线程封闭:开始是只有放入对象的生产者线程能访问可变对象,放入之后生产者不再访问可变对象;后面是只有消费者线程可以访问可变对象,实现了可变对象在线程之间安全的传递。

四、阻塞方法或者中断方法 等待I/O操作结束,等待锁,等待Thread.sleep方法中醒来,等待另一个线程的计算结果(Blocked,Waiting,Timed_Waiting)。方法抛出受检查异常InterruptedException,说明该方法是一个阻塞方法,如果这个方法被中断,它将努力提前结束阻塞状态。抛出该异常后有两个处理策略: 传递异常; 恢复中断状态。

五、同步工具类 1、闭锁(CountDownLatch):countDown方法递减计数器,await方法等待计数器达到零。 2、FutureTask:也可以用作闭锁,FutureTask有三种状态Waiting to run , running , Completed(完成包括正常结束、由于取消而结束、由于异常而结束)。 3、计数信号量(Counting Semaphore),控制同时访问某个特定资源的操作数量,还可用来实现某种资源池,或者对容器施加边界。

转载于:https://my.oschina.net/u/2458458/blog/779566

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值