==================================================== 锁。。 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("逛街"); ~~~~~~~~~~~~~~~~~~忧郁的分割线~~~~~~~~~~~~~~~~~~~~~~~~~~~ CyclicBarrier //不需要显示的开锁 关卡(Barrier)类似于闭锁,他们都能阻塞一组线程,知道某些事件发生,不同之处在于所有CyclicBarrier等待的是现线程,只有一定数目的线程到达这个点时,才允许同时通过。它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用。因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier。CyclicBarrier 支持一个可选的 Runnable 命令,在一组线程中的最后一个线程到达之后(但在释放所有线程之前),该命令只在每个屏障点运行一次。若在继续所有参与线程之前更新共享状态,此屏障操作很有用。 该例子中CyclicBarrier等待两个线程到达后输出conditon is arrive and CycleBarrier is running,两个线程都从await中返回。 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就可以派上用场。CyclicBarrier最重要的属性就是参与者个数,另外最要方法是await()。当所有线程都调用了await()后,就表示这些线程都可以继续执行,否则就会等待。 package examples.ch06.example01; import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class TestCyclicBarrier { // 徒步需要的时间: Shenzhen, Guangzhou, Shaoguan, Changsha, Wuhan private static int[] timeWalk = { 5, 8, 15, 15, 10 }; // 自驾游 private static int[] timeSelf = { 1, 3, 4, 4, 5 }; // 旅游大巴 private static int[] timeBus = { 2, 4, 6, 6, 7 }; static String now() { SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); return sdf.format(new Date()) + ": "; } static class Tour implements Runnable { private int[] times; private CyclicBarrier barrier; private String tourName; public Tour(CyclicBarrier barrier, String tourName, int[] times) { this.times = times; this.tourName = tourName; this.barrier = barrier; } public void run() { try { Thread.sleep(times[0] * 1000); System.out.println(now() + tourName + " Reached Shenzhen"); barrier.await(); Thread.sleep(times[1] * 1000); System.out.println(now() + tourName + " Reached Guangzhou"); barrier.await(); //第二次阻塞了哈 Thread.sleep(times[2] * 1000); System.out.println(now() + tourName + " Reached Shaoguan"); barrier.await(); Thread.sleep(times[3] * 1000); System.out.println(now() + tourName + " Reached Changsha"); barrier.await(); Thread.sleep(times[4] * 1000); System.out.println(now() + tourName + " Reached Wuhan"); barrier.await(); } catch (InterruptedException e) { } catch (BrokenBarrierException e) { } } } public static void main(String[] args) { // 三个旅行团 CyclicBarrier barrier = new CyclicBarrier(3); ExecutorService exec = Executors.newFixedThreadPool(3); exec.submit(new Tour(barrier, "WalkTour", timeWalk)); exec.submit(new Tour(barrier, "SelfTour", timeSelf)); exec.submit(new Tour(barrier, "BusTour", timeBus)); exec.shutdown(); } } ~~~~~~~~~~~~~~~~~~~~~~wait和notify级别的锁~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 显式锁 在java 5之前,用于调节共享对象访问的机制只有synchronized和volatile。java 5提供了新的选择:ReentrantLock。ReentrantLock能够提供更多的高级特性,比如轮询和可定时的加锁,可中断的加锁。以及一个支持读锁和写锁的ReentrantReadWriteLock。使用ReentrantLock必须手动使用lock或其他操作加锁,在finally块中unlock。 Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set (wait-set)。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。 class BoundedBuffer { final Lock lock = new ReentrantLock(); final Condition notFull = lock.newCondition(); final Condition notEmpty = lock.newCondition(); final Object[] items = new Object[100]; int putptr, takeptr, count; public void put(Object x) throws InterruptedException { lock.lock(); try { while (count == items.length) notFull.await(); items[putptr] = x; if (++putptr == items.length) putptr = 0; ++count; notEmpty.signal(); } finally { lock.unlock(); } } public Object take() throws InterruptedException { lock.lock(); try { while (count == 0) notEmpty.await(); Object x = items[takeptr]; if (++takeptr == items.length) takeptr = 0; --count; notFull.signal(); return x; } finally { lock.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(); } } }