• Java多线程与并发库高级应用-工具类介绍


    java.util.concurrent.Lock

    1、Lock比传统线程模型中的synchronized方式更加面向对象,与生活中的锁类似,
    锁本身也应该是一个对象。两个线程执行的代码片段要实现同步互斥的效果,它们必须用同一个Lock对象。

      lock替代synchronized

      

    class Outputer {
            Lock lock = new ReentrantLock();
            public void output(String name) {
                int len = name.length();
                lock.lock();    
                try{
                    for (int i = 0; i < len; i++) {
                        char c = name.charAt(i);
                        System.out.print(c);
                    }
                }finally{
                    lock.unlock();  //这里防止内部代码出现异常,即无论如何最后都会释放锁
                }
                lock.unlock();
                System.out.println();
            }
    }

     

    售票系统

    package com.java.juc;
    
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class TestLock {
    
        public static void main(String[] args) {
            Ticket ticket = new Ticket();
            new Thread(ticket, "窗口1售票").start();
            new Thread(ticket, "窗口2售票").start();
            new Thread(ticket, "窗口3售票").start();
        }
    
    }
    
    class Ticket implements Runnable {
        private int ticket = 100;
        private Lock lock = new ReentrantLock();
    
        @Override
        public void run() {
            while (true) {
                lock.lock();
                try {
                    if (ticket > 0) {
                        Thread.sleep(20);
                        System.out.println(Thread.currentThread().getName()
                                + ",余票量:" + ticket--);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        }
    }

    2、读写锁:

    读写锁:分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,这是由jvm自己控制的,你只要上好相应的锁即可。
    * 如果你的代码只读数据,可以很多人同时读,但不能同时写,那就上读锁;
    * 如果你的代码修改数据,只能有一个人在写,且不能同时读取,那就上写锁。
    * 总之,读的时候上读锁,写的时候上写锁!

    /* 面试题:3个线程读,3个线程写 同一个数据
     */
    public class ReadWriteLockTest {
        public static void main(String[] args) {
            final Queue3 queue = new Queue3();
    
            for(int i = 0;i<3;i++){
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        while(true){
                            queue.get();
                        }
                    }
                }).start();
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        queue.set(new Random().nextInt(10000));
                    }
                }).start();
            }
        }
    
    }
    
    class Queue3{
        private Object data = null; //共享数据 ,只能有一个线程写该数据,但可以有多个线程同时读
        ReadWriteLock rwl = new ReentrantReadWriteLock();  //读写锁
        
        public void get(){
            try {
                rwl.readLock().lock();  //上读锁 可以有多个线程同时读
                System.out.println(Thread.currentThread().getName() + " be ready to read data!");
                Thread.sleep((long)Math.random() * 1000);
                System.out.println(Thread.currentThread().getName() + " have read data : "+ data);
            } catch (InterruptedException e) {
            }finally{
                rwl.readLock().unlock();  //释放读锁
            }
        }
        public void set(Object data){
            try {
                rwl.writeLock().lock();  //添加写锁,保证只能有一个线程进行写操作
                System.out.println(Thread.currentThread().getName() + " be read to write data: "+ data);
                Thread.sleep((long)Math.random() * 1000);
                this.data = data;
                System.out.println(Thread.currentThread().getName() + "has write data");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally{
                rwl.writeLock().unlock();  //释放写锁
            }
        }
    }

     简单的读写锁示例

    package com.java.juc;
    
    import java.util.Random;
    import java.util.concurrent.locks.ReadWriteLock;
    import java.util.concurrent.locks.ReentrantReadWriteLock;
    
    public class TestReadWriteLock {
        public static void main(String[] args) {
            final ReadWriteLockDemo demo = new ReadWriteLockDemo();
            new Thread(new Runnable(){
                @Override
                public void run() {
                    demo.set(new Random().nextInt(5000));
                }
            },"Write").start();
            
            for(int i = 0;i<100;i++){
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        demo.get();
                    }
                }, "Read").start();
            }
        }
    }
    
    class ReadWriteLockDemo{
        private int number = 0;
        
        private ReadWriteLock lock = new ReentrantReadWriteLock();
        
        public void get(){
            try{
                lock.readLock().lock();
                System.out.println(Thread.currentThread().getName() +" "+number);
            }finally{
                lock.readLock().unlock();
            }
        }
        public void set(int number){
            try{
                lock.writeLock().lock();
                this.number = number;
            }finally{
                lock.writeLock().unlock();
            }
        }
    }

     Hibernate的一个面试题:

      User user = session.load(id,User.class);

      User user = session.get(id,User.class);

      以上两个的却别。

      get()方式,直接查询数据库,如果查询到赋值给User对象,如果没有查询到则返回为null

      load()方式,实际上是从User的一个代理中获取, User$Proxy中包含有一个真实的User对象,当调用load()时,如果成员变量User为null,则从数据库查询将记录返回并给User赋值,当load()时User不为null,则直接返回User对象

    /**
     * 面试题: 设计一个缓存系统
     * @author Administrator
     *
     */
    public class CacheDemo {
    
        Map<String, Object> cache = new HashMap<String, Object>();
        public static void main(String[] args) {
    
        }
        
        private ReadWriteLock rwl = new ReentrantReadWriteLock();
        public Object getData(String key){
            rwl.readLock().lock(); 
            Object value = null;
            try {
                value = cache.get(key);
                if(value == null){
                    rwl.readLock().unlock();
                    rwl.writeLock().lock();
                    try {
                        if(value == null){  //防止后边线程加载数据,使用双端检测机制
                            value = "xxx"; //queryDB
                            cache.put(key, value);
                        }
                    }finally{
                        rwl.writeLock().unlock();
                    }
                    rwl.readLock().lock();
                }
            } catch (Exception e) {
            }finally{
                rwl.readLock().unlock();
            }
            return value;
        }
        
    
    }

    ReadWriteLock javaAPI中有缓存的代码:

    class CachedData {
       Object data;
       volatile boolean cacheValid;
       ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
    
       void processCachedData() {
         rwl.readLock().lock();
         if (!cacheValid) {
            // Must release read lock before acquiring write lock
            rwl.readLock().unlock();
            rwl.writeLock().lock();
            // Recheck state because another thread might have acquired
            //   write lock and changed state before we did.
            if (!cacheValid) {
              data = ...
              cacheValid = true;
            }
            // Downgrade by acquiring read lock before releasing write lock
            rwl.readLock().lock();
            rwl.writeLock().unlock(); // Unlock write, still hold read
         }
    
         use(data);
         rwl.readLock().unlock();
       }
     }
    

    3、Condition 实现线程通信

    传统的线程通信方式

    /*
     * 传统线程通信
     * 主线程和子线程分别打印 100次 和 10次,循环50次
     */
    public class TraditionalThreadCommunication2 {
    
        public static void main(String[] args) {
            final Buiness buiness = new Buiness();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for(int i = 1;i<=50;i++){
                        buiness.sub(i);
                    }
                }
            }).start();
            
            for(int i = 1;i<=50;i++){
                buiness.main(i);
            }
        }
        
        static class Buiness{
            private boolean isShouldSub = false;  //主线程先打印
            public synchronized void main(int j){ //进行同步,防止在打印时被其他线程干扰
                while(isShouldSub){  //这里使用while 防止假唤醒
                    try {
                        this.wait();  //wait() 和 notify() 必须出现在synchronized同步中
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                for(int i = 1;i<=100;i++){
                    System.out.println("main thread print "+ i + " loop of " + j);
                }
                isShouldSub = true;
                this.notify();
            }
            public synchronized void sub(int j){
                while(!isShouldSub){
                    try {
                        this.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                for(int i = 1 ; i<=10;i++){
                    System.out.println("sub thread print "+ i + " loop of " + j);
                }
                isShouldSub = false;
                this.notify();
            }
        }
    
    }

    将上述程序改写为使用Condition

    /*
     * 传统线程通信
     * 主线程和子线程分别打印 100次 和 10次,循环50次
     * 改写成使用 Condition 的方式
     */
    public class TraditionalThreadCommunication2 {
    
        public static void main(String[] args) {
            final Buiness buiness = new Buiness();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for(int i = 1;i<=50;i++){
                        buiness.sub(i);
                    }
                }
            }).start();
            
            for(int i = 1;i<=50;i++){
                buiness.main(i);
            }
        }
        
        /**
         * 将程序改写为使用Lock&Condition的方式进行 同步和通信
         * @author Administrator
         *
         */
        static class Buiness{
            Lock lock = new ReentrantLock();
            Condition condition = lock.newCondition();
            private boolean isShouldSub = false;  //主线程先打印
            public void main(int j){ //进行同步,防止在打印时被其他线程干扰
                lock.lock();
                try {
                    while(isShouldSub){  //这里使用while 防止假唤醒
                        try {
                            condition.await();
    //                    this.wait();  //wait() 和 notify() 必须出现在同步监视器内部中
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                    for(int i = 1;i<=100;i++){
                        System.out.println("main thread print "+ i + " loop of " + j);
                    }
                    isShouldSub = true;
                    condition.signal();
    //            this.notify(); 
                } finally {
                    lock.unlock();
                }
            }
            public void sub(int j){
                lock.lock();
                try {
                    while(!isShouldSub){
                        try {
                            condition.await();
    //                    this.wait();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                    for(int i = 1 ; i<=10;i++){
                        System.out.println("sub thread print "+ i + " loop of " + j);
                    }
                    isShouldSub = false;
                    condition.signal();
    //            this.notify();
                } finally{
                    lock.unlock();
                }
            }
        }
    
    }

    main thread print 1 loop of 1
    main thread print 2 loop of 1
    main thread print 3 loop of 1
    main thread print 4 loop of 1
    main thread print 5 loop of 1
    main thread print 6 loop of 1
    main thread print 7 loop of 1
    ...
    main thread print 99 loop of 1
    main thread print 100 loop of 1
    sub thread print 1 loop of 1
    sub thread print 2 loop of 1
    sub thread print 3 loop of 1
    sub thread print 4 loop of 1
    sub thread print 5 loop of 1
    sub thread print 6 loop of 1
    sub thread print 7 loop of 1
    sub thread print 8 loop of 1
    sub thread print 9 loop of 1
    sub thread print 10 loop of 1
    main thread print 1 loop of 2
    main thread print 2 loop of 2
    main thread print 3 loop of 2
    main thread print 4 loop of 2
    main thread print 5 loop of 2
    main thread print 6 loop of 2
    main thread print 7 loop of 2
    main thread print 8 loop of 2
    main thread print 9 loop of 2
    ...
    main thread print 99 loop of 2
    main thread print 100 loop of 2
    sub thread print 1 loop of 2
    sub thread print 2 loop of 2
    sub thread print 3 loop of 2
    sub thread print 4 loop of 2
    sub thread print 5 loop of 2
    sub thread print 6 loop of 2
    sub thread print 7 loop of 2
    sub thread print 8 loop of 2
    sub thread print 9 loop of 2
    sub thread print 10 loop of 2
    main thread print 1 loop of 3
    main thread print 2 loop of 3
    main thread print 3 loop of 3

    ...

    使用Condition比传统的好处

    可以实现多路Condition ,在javaAPI中有

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

    使用多路Condition,可以扩展上述的一个例子,老大打印完 -> 老二   老二-> 老三  老三-> 老大  老大-> 老二...

    /**
     * 第一个线程循环100次,第二个线程循环10次,第三个线程循环20次,如此循环50次,请写出程序 这里使用Condition
     * 
     * @author Administrator
     * 
     */
    public class ThreeConditionCommunication {
    
        public static void main(String[] args) {
            final Business2 business = new Business2();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 1; i <= 50; i++) {
                        business.sub2(i);
                    }
                }
            }).start();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 1; i <= 50; i++) {
                        business.sub3(i);
                    }
                }
            }).start();
    
            for (int i = 1; i <= 50; i++) {
                business.main(i);
            }
        }
    
    }
    
    class Business2 {
        Lock lock = new ReentrantLock();
        Condition condition1 = lock.newCondition();
        Condition condition2 = lock.newCondition();
        Condition condition3 = lock.newCondition();
        private int shoudeSub = 1;
    
        public void sub2(int i) {
            lock.lock();
            try {
                while (shoudeSub != 2) { // 这里也可以用 if ,用while比较好一些 As in the one argument
                                        // version, interrupts and spurious wakeups are
                                        // possible, and this method should always be
                                        // used in a loop
                    try { // 防止线程有可能被假唤醒 (while放在这里提现了水准)
                        condition2.await();  //等待
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                for (int j = 1; j <= 100; j++) {
                    System.out.println("sub2 thread sequence of " + j + ", loop of " + i);
                }
                shoudeSub = 3;
                condition3.signal();//唤醒
            } finally{
                lock.unlock();
            }
        }
        public void sub3(int i) {
            lock.lock();
            try {
                while (shoudeSub != 3) { // 这里也可以用 if ,用while比较好一些 As in the one argument
                    // version, interrupts and spurious wakeups are
                    // possible, and this method should always be
                    // used in a loop
                    try { // 防止线程有可能被假唤醒 (while放在这里提现了水准)
                        condition3.await();  //等待
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                for (int j = 1; j <= 20; j++) {
                    System.out.println("sub3 thread sequence of " + j + ", loop of " + i);
                }
                shoudeSub = 1;
                condition1.signal();//唤醒
            } finally{
                lock.unlock();
            }
        }
    
        public void main(int i) {
            lock.lock();
            try {
                while (shoudeSub != 1) {
                    try {
                        condition1.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                for (int j = 1; j <= 10; j++) {
                    System.out.println("main thread sequence of " + j + ", loop of "
                            + i);
                }
                shoudeSub = 2;
                condition2.signal();
            } finally{
                lock.unlock();
            }
        }
        /**
         * 
         * synchronized (obj) { 这里的obj与obj.wait必须相同,否则会抛异常 while (<condition does
         * not hold>) obj.wait(); ... // Perform action appropriate to condition }
         */
    }

    Condition的一个例子:

     编写一个程序,开启3个线程 ,这三个线程的ID分别为 A,B, C,每个线程将自己的ID 在屏幕上打印10遍,要求输出的结果必须按顺序显示。

    如:ABCABCABC.....依次递归

    这里实现了一个比题目稍微难得例子,A 打印10次,B打印20次 ,C打印5次依次递归20次。

    package com.java.juc;
    
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class TestAlternative {
    
        public static void main(String[] args) {
            final Alternative alternative = new Alternative();
            new Thread(new Runnable(){
                @Override
                public void run() {
                    for(int i = 1;i<=20;i++){
                        alternative.loopA(i);
                    }
                }
            },"A").start();
            new Thread(new Runnable(){
                @Override
                public void run() {
                    for(int i = 1;i<=20;i++){
                        alternative.loopB(i);
                    }
                }
            },"B").start();
            new Thread(new Runnable(){
                @Override
                public void run() {
                    for(int i = 1;i<=20;i++){
                        alternative.loopC(i);
                        System.out.println("-----------------");
                    }
                }
            },"C").start();
            
        }
    
    }
    
    class Alternative{
        
        private int number = 1;
        private Lock lock = new ReentrantLock();
        private Condition condition1 = lock.newCondition();
        private Condition condition2 = lock.newCondition();
        private Condition condition3 = lock.newCondition();
        
        void loopA(int outerLoop){
            lock.lock();
            try{
                while(number != 1){
                    condition1.await();
                }
                for(int i = 1;i<=10;i++){
                    System.out.println(Thread.currentThread().getName() + "	" + i + "	" + outerLoop);
                }
                number = 2;
                condition2.signal();
            }catch(Exception e){
            }finally {
                lock.unlock();
            }
        }
        
        void loopB(int outerLoop){
            lock.lock();
            try{
                while(number != 2){
                    condition2.await();
                }
                for(int i = 1;i<=20;i++){
                    System.out.println(Thread.currentThread().getName() + "	" + i + "	" + outerLoop);
                }
                number = 3;
                condition3.signal();
            }catch(Exception e){
                
            }finally{
                lock.unlock();
            }
        }
        void loopC(int outerLoop){
            lock.lock();
            try{
                while(number != 3){
                    condition3.await();
                }
                for(int i = 1;i<=5;i++){
                    System.out.println(Thread.currentThread().getName() + "	" + i + "	" + outerLoop);
                }
                number = 1;
                condition1.signal();
            }catch(Exception e){
                
            }finally{
                lock.unlock();
            }
        }
    }

    java5的Semaphere同步工具

      Semaphore实现信号灯

      Semaphore可以维护当前访问自身的线程个数,并提供了同步机制。使用Semaphore可以控制同时访问资源的线程个数,例如,实现一个文件允许的并发访问数。

      假设一个文件同时可以被3个人访问,来了5个人,同时只有3个访问。3个中任何一个出来后,等待的就可以进去了。

    public class SemaphoreTest {
    
        public static void main(String[] args) {
            ExecutorService service = Executors.newCachedThreadPool();
            final  Semaphore sp = new Semaphore(3);  //还有一个构造方法,Semaphore(int permits, boolean fair)  fair参数为true表示谁先来谁先进,一种公平的原则 
            for(int i=0;i<10;i++){
                Runnable runnable = new Runnable(){
                        public void run(){
                        try {
                            sp.acquire();
                        } catch (InterruptedException e1) {
                            e1.printStackTrace();
                        }
                        System.out.println("线程" + Thread.currentThread().getName() + 
                                "进入,当前已有" + (3-sp.availablePermits()) + "个并发");
                        try {
                            Thread.sleep((long)(Math.random()*10000));
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println("线程" + Thread.currentThread().getName() + 
                                "即将离开");                    
                        sp.release();
                        //下面代码有时候执行不准确,因为其没有和上面的代码合成原子单元
                        System.out.println("线程" + Thread.currentThread().getName() + 
                                "已离开,当前已有" + (3-sp.availablePermits()) + "个并发");                    
                    }
                };
                service.execute(runnable);            
            }
        }
    }

    单个信号量的Semaphore对象可以实现互斥锁的功能,并且可以是由一个线程获得了 "锁",再由另外一个线程释放"锁",这可应用于死锁恢复的一些场合。

    java5 的CyclicBarrier同步工具

    /**
     * 表示大家彼此等待,大家集合好后才开始出发,分散活动后又在指定地点集合碰面,
     * 这就好比整个公司的人员利用周末时间集体郊游一样,先各自从家出发到公司集合后,
     * 再同时出发到公园游玩,在指定地点集合后再同时开始就餐,…。
     * @author Administrator
     *
     */
    public class CyclicBarrierTest {
    
        public static void main(String[] args) {
            ExecutorService threadPool = Executors.newCachedThreadPool();
            final CyclicBarrier cb = new CyclicBarrier(3);
            for (int i = 0; i < 3; i++) {
                Runnable runnable = new Runnable() {
                    public void run() {
                        try {
                            Thread.sleep((long) (Math.random() * 10000));
                            System.out.println("线程"+ Thread.currentThread().getName()
                                            + "即将到达集合点1,当前已有 "+(cb.getNumberWaiting()+1) +"个已经到达,"+(cb.getNumberWaiting() == 2?"都到齐了,继续走啊":"正在等待"));
                            cb.await();
                            
                            Thread.sleep((long) (Math.random() * 10000));
                            System.out.println("线程"+ Thread.currentThread().getName()
                                    + "即将到达集合点2,当前已有 "+(cb.getNumberWaiting()+1) +"个已经到达,"+(cb.getNumberWaiting() == 2?"都到齐了,继续走啊":"正在等待"));
                            cb.await();
                            
                            Thread.sleep((long) (Math.random() * 10000));
                            System.out.println("线程"+ Thread.currentThread().getName()
                                    + "即将到达集合点3,当前已有 "+(cb.getNumberWaiting()+1) +"个已经到达,"+(cb.getNumberWaiting() == 2?"都到齐了,继续走啊":"正在等待"));
                            cb.await();
                        } catch (Exception e) {
                            // TODO: handle exception
                        }
                    }
                };
                threadPool.execute(runnable);
            }
            threadPool.shutdown();
        }
    
    }

    java5的CountDownLatch同步工具

      CountDownLatch : 闭锁,在完成某些运算时,只有其他所有线程的运算全部完成,当前运算才继续执行

      CountDownLatch应用1:比如要统计5个线程并发的运行时间,即线程的开始时间与最后一个线程的运行结束时间的间隔时间。

      

    package com.java.juc;
    
    import java.util.concurrent.CountDownLatch;
    
    public class TestCountDownLatch2 {
    
        public static void main(String[] args) {
    
            CountDownLatch latch = new CountDownLatch(5);
            LatchDemo2 ld = new LatchDemo2(latch);
            
            long start = System.currentTimeMillis();
            for(int i = 0;i<5;i++){
                new Thread(ld).start();
            }
            try {
                latch.await();   //先执行完成的线程需要等待还没有执行完的线程
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
            long end  = System.currentTimeMillis();
            System.out.println("cost: "+ (end - start));
        }
    
    }
    
    class LatchDemo2 implements Runnable{
        private CountDownLatch latch;
        
        public LatchDemo2(CountDownLatch latch) {
            this.latch = latch;
        }
        
        @Override
        public void run() {
    
            try {
                synchronized(this){
                    for(int i = 0;i<50000;i++){  //找出50000以内的所有偶数
                        if(i % 2 == 0){
                            System.out.println(i);
                        }
                    }
                }
            } finally{
                latch.countDown();   //为了让这一句一定执行可以放在finally中
            }
        }
    }

      还可以应用于计算所有种类商品的平均销售总和,平均销售时间等,如果使用单线程计算效率非常低,相当于是串行计算。可以使用并行计算,按照商品种类进行区分并行的计算。可以将最终的每个线程的计算结果在进行汇总,可以得出最终的的总的销售数据,这就可以使用CountDownLatch进行操作,可以大幅度提高效率。(京东)

    应用:运动员跑步比赛,得到最终的排名需要在所有运动员都完成之后,公布最终的结果。

    /**
     * 犹如倒计时计数器,调用CountDownLatch对象的countDown方法就将计数器减一,
     * 当计数器到达0时,则所有等待者或单个等待者开始执行。
     * 可以实现一个人(也可以是多个人)等待其他所有人都来通知他,可以实现一个人通知多个人的效果,
     * 类似裁判一声口令,运动员同时开始奔跑,或者所有运动员都跑到
     * 终点后裁判才可以公布结果。还可以实现一个计划需要多个领导都签字后
     * 才能继续向下实施的情况
     * @author Administrator
     *
     */
    public class CountDownLetchTest {
    
        public static void main(String[] args) {
            ExecutorService executorService = Executors.newCachedThreadPool();
            final CountDownLatch cdOrder = new CountDownLatch(1); //计数器初始值 1
            final CountDownLatch cdAnswer = new CountDownLatch(3);
            for(int i = 0;i<3;i++){
                Runnable runnable = new Runnable() {
                    
                    @Override
                    public void run() {
                        try {
                            System.out.println("线程"+Thread.currentThread().getName()
                                    +"正准备接受命令");
                            cdOrder.await();
                            System.out.println("线程"+Thread.currentThread().getName()
                                    +"已接受命令");
                            Thread.sleep((long)(Math.random()*10000));
                            System.out.println("线程"+Thread.currentThread().getName()
                                    +"回应命令处理结果");
                            cdAnswer.countDown();
                        } catch (Exception e) {
                            // TODO: handle exception
                        }
                    }
                };
                executorService.execute(runnable);
            }
            try {
                Thread.sleep((long)(Math.random()*10000));
                System.out.println("线程"+Thread.currentThread().getName()
                        +"即将发布命令");
                cdOrder.countDown();  //计数器数值减 1
                System.out.println("线程"+Thread.currentThread().getName()
                        +"已发送命令,正在等待结果");
                cdAnswer.await();
                System.out.println("线程"+Thread.currentThread().getName()
                        +"已收到所有响应结果");
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            executorService.shutdown();
        }
    }

     java 中CycliBarriar 和 CountDownLatch 有什么区别?

      这两个的区别是CyclicBarrier 可以重复使用已经通过的障碍,而 CountdownLatch 不能重复使用。

    java5的Exchanger同步工具

    /**
     * 用于实现两个人之间的数据交换,每个人在完成一定的事物后想与对方交换数据,第一个先拿出数据的人将
     * 一直等待第二个人拿着数据到来时,才能彼此交换数据。
     * @author Administrator
     *
     */
    public class ExchangerTest {
    
        public static void main(String[] args) {
            ExecutorService executorService = Executors.newCachedThreadPool();
            final Exchanger exchanger = new Exchanger();
            executorService.execute(new Runnable() {
    
                @Override
                public void run() {
                    try {
                        String data1 = "aaa";
                        System.out.println("线程" + Thread.currentThread().getName()
                                + "正在把数据" + data1 + "换出去");
                        Thread.sleep((long) (Math.random() * 10000));
                        String data2 = (String) exchanger.exchange(data1);
                        System.out.println("线程" + Thread.currentThread().getName()
                                + "换回的数据为 " + data2);
                    } catch (Exception e) {
                        // TODO: handle exception
                    }
                }
            });
            executorService.execute(new Runnable() {
    
                @Override
                public void run() {
                    try {
                        String data1 = "bbb";
                        System.out.println("线程" + Thread.currentThread().getName()
                                + "正在把数据" + data1 + "换出去");
                        Thread.sleep((long) (Math.random() * 10000));
                        String data2 = (String) exchanger.exchange(data1);
                        System.out.println("线程" + Thread.currentThread().getName()
                                + "换回的数据为 " + data2);
                    } catch (Exception e) {
                        // TODO: handle exception
                    }
                }
            });
        }
    
    }

    打印结果为:

    线程 pool-1-thread-1正把数据 aaa 换出去

    线程 pool-1-thread-2正把数据 bbb 换出去

    线程 pool-1-thread-2换回的数据为 aaa

    线程 pool-1-thread-1换回的数据为 bbb

  • 相关阅读:
    获取Web打印的实际打印次数
    Web打印的在线设计
    上海交易所的STEP/FIX/FAST协议解析
    回忆我的姑妈
    指定Web打印的打印机
    Web打印控件设计
    最终用户在线设计和修改Web报表
    NOIP2021 游记
    gym103427 部分题解
    gym103428 部分题解
  • 原文地址:https://www.cnblogs.com/wq3435/p/6037082.html
Copyright © 2020-2023  润新知