`
mshijie
  • 浏览: 96274 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

java并发编程-构建块

阅读更多

java并发编程-Executor框架

java5引入了很多新的并发容器和工具,极大的简化了并发程序的编写。本文先介绍Collections.synchronizedXXX工厂方法创建的同步容器的不足,再介绍ConcurrentHashMap,CopyOnWriterArrayList,BlockingQueue,CountDownLatch,Semaphore,CyclicBarrier和显示锁类。

 

一、引言

所有的并发问题都来源于如何协调访问并发状态,可变状态越少,并发控制也就越容易容易。没有可变状态的对象永远是现成安全。对有可变状态的对象的并发访问必须进行加锁,对每个方法都加锁并不能保证并发访问一定处于一致的状态。在构建并发程序的时候,可以把并发的控制代理到已有的并发类,比如类库提供的并发容器或工具。

二、同步容器的不足

对Collections.synchronizedXXX工厂方法创建的同步容器,每个方法都是使用该容器的内部锁进行控制的,这会带来一性能问题,因为多个安全的读操作也要等待排它锁。即使每个方法的调用都是线程安全的,但同时调用多个操作室,不一定是线程安全的,比如缺少即添加,相等即修改等炒作,是两步原子操作构成的,和在一起并不是原子的,必须在调用代码里使用容器的锁进行控制。另外在迭代集合的时候,还会由于并发修改而抛出ConcurrentModefiedExceptioin,这往往是调用程序不希望的结果。

三、ConcurrentHashMap,CopyOnWriterArrayList

ConcurrentHashMap使用的内部细粒度的分离锁,这个锁机制允许任意数量的读线程并发访问,提高了吞吐率。在迭代时不会抛出ConcurrentModefiedExceptioin,如果在迭代期间有修改发生,返回的是迭代开始时的状态。另外对缺少即添加,相等即修改等二元操作也有相应的方法支持。ConcurrentHashMap实现了ConcurrentMap提供的几个特殊原子操作:

public V putIfAbsent(K key,  V value)

如果指定键已经不再与某个值相关联,则将它与给定值关联。

public boolean remove(Object key, Object value)

只有目前将键的条目映射到给定值时,才移除该键的条目。

public boolean replace(K key, V oldValue, V newValue)

只有目前将键的条目映射到给定值时,才替换该键的条目。

public V replace(K key, V value)

只有目前将键的条目映射到某一值时,才替换该键的条目。

 

CopyOnWriterArrayList 是ArrayList的一个并发替代品,通常情况下,它提供比较好的并发性,允许多个现在并发的对其进行迭代。每次需要修改时,便会创建并重新发布一个新的容器拷贝,以此来实现可变性。因为底层使用数组实现,如果数组元素较多是,复制多付出的代价较大。

三、BlockingQueue

阻塞队列提供了可以阻塞的put和take方法,以及与之等价的可以指定超时的offer和poll。如果Queue是空的,那么take方法会一直阻塞,直到有元素可用。如果Queue是有线长度的,队列满的时候put方法也会阻塞。BlockingQueue可以很好的支持生产者和消费者模式,生产者往队列里put,消费者从队列里get,两者能够得好很好的同步。BlockingQueue的实现类LinkedBlockingQueue和ArrayBlockingQueue是FIFO队列,PriorityBlockingQueue是一个按优先级排序的队列。使用BlockingQueue构建的一个生产者与消费例子:

消费者:

public class Consumer implements Runnable {

	private BlockingQueue<Food> queue;
	private ExecutorService exec;

	public Consumer(BlockingQueue<Food> queue, ExecutorService exec) {
		this.queue = queue;
		this.exec = exec;
	}

	@Override
	public void run() {
		while (!exec.isShutdown()) {
			try {
				Thread.sleep(2000);
				Food food = queue.take();
				System.out.println("Consumer " + food);
			} catch (InterruptedException e) {
				e.printStackTrace();
			} catch (RejectedExecutionException e) {

			}
		}
	}
}

 生产者:

public class Producer implements Runnable {

	private BlockingQueue<Food> queue;
	private ExecutorService exec;

	public Producer(BlockingQueue<Food> queue, ExecutorService exec) {
		this.queue = queue;
		this.exec = exec;
	}

	@Override
	public void run() {
		while (!exec.isShutdown()) {
			Food food = new Food();
			try {
				Thread.sleep(4000);
				queue.put(food);
				System.out.println("Produce " + food);
			} catch (InterruptedException e) {
				e.printStackTrace();
			} catch (RejectedExecutionException e) {

			}
		}
	}
}

 Main:

		BlockingQueue<Food> queue = new ArrayBlockingQueue<Food>(5);
		ExecutorService exec = Executors.newFixedThreadPool(3);
		Producer p1 = new Producer(queue, exec);
		Producer p2 = new Producer(queue, exec);

		Consumer c1 = new Consumer(queue, exec);
	
		exec.execute(p1);
		exec.execute(p2);
		exec.execute(c1);
		try {
			Thread.sleep(10000);
		} catch (InterruptedException ignored) {
		}
		exec.shutdown();

 四、CountDownLatch

闭锁(Latch),它可以延迟线程的进度知道线程到达终止状态。一个闭锁工作方式就像一道门,直到闭锁到达终点状态之前,门一直关闭着。终点状态到了之后,所有阻塞的线程都可以通过。CountDownLatch 使用一个计数器作为终点状态,知道计数器的值到达0时,闭锁才会打开。调用await 方法,线程会阻塞知道计数器为0,countDown 方法使计数器减一。

闭锁有两种常见的用法,开始闭锁,结束闭锁。开始闭锁用于等待一个条件到达后所有线程一起执行,结束闭锁可以用来等待所有条件或所有线程结束后再进行后续处理。例子:

final CountDownLatch startLatch = new CountDownLatch(1);
final CountDownLatch endLatch = new CountDownLatch(3);
Runnable prepare = new Runnable() {
	@Override
	public void run() {
		try {
			startLatch.await();//等待开始闭锁,线程同时开始执行
			System.out.println("收拾东西,准备出门");
			Random rnd = new Random();
			Thread.sleep(rnd.nextInt(1000));
		} catch (InterruptedException ignored) {
		}
		endLatch.countDown();
	}
};

Thread mum = new Thread(prepare);
Thread dad = new Thread(prepare);
Thread me = new Thread(prepare);
mum.start();
dad.start();
me.start();
startLatch.countDown();
try {
	endLatch.await();
} catch (InterruptedException ignored) {
}
System.out.println("逛街");

 五、Semaphore,信号量

使用信号量进行同步和互斥的控制是最经典的并发模型,java中也提高支持。一个Semaphore管理一个有效的许可 集,许可基的数量通过构造函数传入,通过acquire方法申请一个许可,许可数为0则阻塞线程,否则许可数减一,使用release方法释放一个许个,许可数加一。一个技术量为1的Semaphore为二元信号量,相当于一个互斥锁,表示不可重入的锁。一个使用信号量控制并发容器上届的例子:

public class BoundedHashSet<T> {
	private final Set<T> set;
	private final Semaphore sem;

	public BoundedHashSet(int bound) {
		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

关卡(Barrier)类似于闭锁,他们都能阻塞一组线程,知道某些事件发生,不同之处在于所有CyclicBarrier等待的是现线程,只有一定数目的线程到达这个点时,才允许同时通过。它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用。因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier。CyclicBarrier 支持一个可选的 Runnable 命令,在一组线程中的最后一个线程到达之后(但在释放所有线程之前),该命令只在每个屏障点运行一次。若在继续所有参与线程之前更新共享状态,此屏障操作很有用。

public class Main {

	public static CyclicBarrier getCyclicBarrier(int count) {
		if (count <= 0)
			return null;
		final CyclicBarrier cyclicBarrier = new CyclicBarrier(count,
				new Runnable() {
					public void run() {
						try {
							Thread.sleep(1000);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
						System.out.println("conditon is arrive and CycleBarrier is running");
					}
				});
		return cyclicBarrier;
	}

	public static Thread getThread(String nameOfThread,
			final CyclicBarrier cyclicBarrier) {
		Thread thread = new Thread(nameOfThread) {
			public void run() {
				System.out.println(this.getName() +
"is begin; and count is "+ (++count));
				try {
					cyclicBarrier.await();
				} catch (InterruptedException e) {
					e.printStackTrace();
				} catch (BrokenBarrierException e) {
					e.printStackTrace();
				}
				System.out.println(this.getName() + "finished");
			}
		};
		return thread;

	}

	static int count = 0;

	public static void main(String[] args) {
		/** define a cyclicBarrier and number of barrier is 2. */
		CyclicBarrier cyclicBarrier = getCyclicBarrier(2);
		Thread threadOne = getThread("threadOne", cyclicBarrier);
		threadOne.start();
		Thread threadTwo = getThread("threadTwo", cyclicBarrier);
		threadTwo.start();
	}
}

 该例子中CyclicBarrier等待两个线程到达后输出conditon is arrive and CycleBarrier is running,两个线程都从await中返回。

七、显式锁

 

在java 5之前,用于调节共享对象访问的机制只有synchronized和volatile。java 5提供了新的选择:ReentrantLock。ReentrantLock能够提供更多的高级特性,比如轮询和可定时的加锁,可中断的加锁。以及一个支持读锁和写锁的ReentrantReadWriteLock。使用ReentrantLock必须手动使用lock或其他操作加锁,在finally块中unlock。

ReentrantLock:一个可重入的互斥锁Lock,它具有与使用synchronized方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。 使用ReentrantLock构建的同步Map:

public class LockedMap<K, V> {
	private Map<K, V> map;
	private Lock lock = new ReentrantLock();
	
	public LockedMap(Map<K, V> map) {
		this.map = map;
	}

	public V get(K key) {
		lock.lock();
		try {
			return map.get(key);
		} finally {
			lock.unlock();
		}
	}

	public void put(K key, V value) {
		lock.lock();
		try {
			map.put(key, value);
		} finally {
			lock.unlock();
		}
	}
}
public class ReentrantLockTest {

    private List<Integer> numbers = new ArrayList<Integer>();
    private Lock numbersLock = new ReentrantLock();

    public void addNumbers(int num) {
        try {
            numbersLock.lock();
            numbers.add(num);
        } finally {
            numbersLock.unlock();
        }
    }

    public void outputNumbers() {
        try {
            if (numbersLock.tryLock(1, TimeUnit.SECONDS)) {
                for (int num : numbers) {
                    System.out.println(num);
                }
            }
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        } finally {
            numbersLock.unlock();
        }
    }    

    public static void main(String[] args) {
        final ReentrantLockTest test = new ReentrantLockTest();
        Executor pool = Executors.newFixedThreadPool(3);
        pool.execute(new Runnable() {

            public void run() {
                Random rnd = new Random();
                while (true) {
                    int number = rnd.nextInt();
                    test.addNumbers(number);
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException ignored) {
                    }
                }
            }
        });

        pool.execute(new Runnable() {

            public void run() {
                while (true) {
                    test.outputNumbers();
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException ignored) {
                    }
                }
            }
        });
    }


}
 

ReentrantReadWriteLock提供了对读锁和写锁的支持,同一时刻,可允许多个读锁,但只允许有一个写锁,读锁的获取和写锁的获取是互斥的。从ReentrantReadWriteLock对象的readLock方法可以获得相应的读锁,writeLock方法可以获得相应的写锁。使用ReentrantReadWriteLock构建的Map,允许多个get操作并发执行:

public class ReadWriteMap<K,V>  {
	private Map<K,V> map;
	private ReadWriteLock lock = new ReentrantReadWriteLock();
	private Lock readLock = lock.readLock();
	private Lock writeLock  = lock.writeLock();	
	
	public ReadWriteMap(Map<K,V> map){
		this.map = map;
	}
	
	public V get(K key){
		readLock.lock();
		try{
			return map.get(key);
		}
		finally{
			readLock.unlock();
		}	
	}
	
	public void put(K key,V value){
		writeLock.lock();
		try{
			map.put(key, value);
		}
		finally{
			writeLock.unlock();
		}
	}
	
}
 

所有代码见附件。本文参考《Java并发编程实践 》。

5
0
分享到:
评论
3 楼 feng22253520 2009-12-03  
能不能把Java并发编程实践这本书或源代码发到我的邮箱上啊!!!398639289@qq.com
2 楼 mercyblitz 2009-05-08  
引用
另外在迭代集合的时候,还会由于并发修改而抛出ConcurrentModefiedExceptioin,这往往是调用程序不希望的结果。


最近就发现了这个问题,代码设计的陷阱!
1 楼 jianchen 2009-04-14  
并发编程好强大!期待后续文章。

相关推荐

    Java并发编程-线程安全与基础构建模块

    本文将深入探讨"Java并发编程-线程安全与基础构建模块"这一主题,旨在帮助开发者理解如何有效地处理并发问题,提高程序性能和稳定性。 首先,线程安全是并发编程中的核心概念,指的是多个线程访问同一资源时,无论...

    JAVA并发编程实践-构建执行程序块-学习笔记

    JAVA并发编程实践-构建执行程序块-学习笔记 JAVA并发编程实践是指在JAVA编程语言中,使用多线程、并发编程来实现高效、可靠的程序执行。构建执行程序块是指在并发编程中,使用线程安全的类来管理状态,以确保程序的...

    java并发编程实战源码,java并发编程实战pdf,Java

    《Java并发编程实战》是Java并发编程领域的一本经典著作,它深入浅出地介绍了如何在Java平台上进行高效的多线程编程。这本书的源码提供了丰富的示例,可以帮助读者更好地理解书中的理论知识并将其应用到实际项目中。...

    Java高并发编程,构建并发编程知识体系,提升面试成功率

    Java高并发编程,构建并发编程知识体系,提升面试成功率,完整版17章视频教程下载。 本课程将结合大量图示及代码演示,带你掌握多线程并发编程(线程安全,线程调度,线程封闭,同步容器等)与高并发处理思路与手段...

    Java 并发编程实战.pdf

    《Java并发编程实战》这本书是关于Java语言中并发编程技术的经典著作。它详细介绍了如何在Java环境中有效地实现多线程程序和并发控制机制。在Java平台上,由于其本身提供了强大的并发编程支持,因此,掌握并发编程...

    java并发编程实战中文加英文版加源码

    你丫有没有良知,书籍是什么,是希望,是神圣的,你们这些译者简直就是在犯罪 ,不过要是英文功底不好,还是建议买本看吧,谁让你英文水平不如他们呢 《JAVA并发编程实践》随着多核处理器的普及,使用并发成为构建高...

    Java并发编程实战华章专业开发者书库 (Tim Peierls 等 美Brian Goetz).pdf

    《Java并发编程实战》是一本深入探讨Java平台并发编程的权威指南,由Tim Peierls等人与Brian Goetz合著,旨在帮助Java开发者理解和掌握在多线程环境中编写高效、安全的代码。这本书由拥有丰富经验的JDK并发大师及...

    java并发编程实战(英文版)

    ### Java并发编程实战知识点概述 #### 一、Java并发特性详解 在《Java并发编程实战》这本书中,作者深入浅出地介绍了Java 5.0和Java 6中新增的并发特性。这些特性旨在帮助开发者更高效、安全地编写多线程程序。书中...

    JAVA并发编程实践.pdf+高清版+目录 书籍源码

    《JAVA并发编程实践》这本书是Java开发者深入理解并发编程的重要参考资料。它涵盖了Java并发的核心概念、工具和最佳实践,旨在帮助读者在多线程环境下编写高效、安全的代码。 并发编程是现代软件开发中的关键技能,...

    java并发编程书籍

    Java并发编程是软件开发中的一个关键领域,尤其是在大型企业级应用和分布式系统中。通过学习相关的书籍,开发者可以深入理解如何有效地设计和实现高效的多线程应用程序,避免并发问题,如竞态条件、死锁、活锁等。...

    java并发编程实践高清中文版+源码

    《JAVA并发编程实践》随着多核处理器的普及,使用并发成为构建高性能应用程序的关键。Java 5以及6在开发并发程序中取得了显著的进步,提高了Java虚拟机的性能以及并发类的可伸缩性,并加入了丰富的新并发构建块。在...

    JAVA并发编程实践

    《JAVA并发编程实践》随着多核处理器的普及,使用并发成为构建高性能应用程序的关键。Java 5以及6在开发并发程序中取得了显著的进步,提高了Java虚拟机的性能以及并发类的可伸缩性,并加入了丰富的新并发构建块。在...

    java并发编程与实践

    "Java并发编程与实践"文档深入剖析了这一主题,旨在帮助开发者理解和掌握如何在Java环境中有效地实现并发。 并发是指在单个执行单元(如CPU)中同时执行两个或更多任务的能力。在Java中,这主要通过线程来实现,...

    java并发编程实践(中文版pdf全部40M分2部分上传)2

    《JAVA并发编程实践》既能够成为读者的理论支持,又可以作为构建可靠的、可伸缩的、可维护的并发程序的技术支持。《JAVA并发编程实践》并不仅仅提供并发API的清单及其机制,还提供了设计原则、模式和思想模型,使...

    Java并发编程实战

    1.1 并发简史 1.2 线程的优势 1.2.1 发挥多处理器的强大能力 1.2.2 建模的简单性 1.2.3 异步事件的简化处理 1.2.4 响应更灵敏的用户界面 1.3 线程带来的风险 1.3.1 安全性问题 1.3.2 活跃性问题 1.3.3 ...

    Java并发编程实践--电子书.rar

    《Java并发编程实践》这本书是Java开发者深入理解并发编程的重要参考资料。...通过阅读这本书,你可以深入理解Java并发编程的理论和实践,提升你的编程能力,为构建高并发、高性能的系统打下坚实基础。

    JAVA并发编程实践.pdf

    《JAVA并发编程实践》随着多核处理器的普及,使用并发成为构建高性能应用程序的关键。Java 5以及6在开发并发程序中取得了显著的进步,提高了Java虚拟机的性能以及并发类的可伸缩性,并加入了丰富的新并发构建块。在...

    Java并发编程相关资料

    Java并发编程是Java开发中的重要领域,涉及到多线程、同步机制、线程池等多个核心概念,对于构建高效、稳定的应用至关重要。这份资料包含了关于Java并发编程的博客和网页,可以提供深入的理解和实践指导。 在Java...

Global site tag (gtag.js) - Google Analytics