zoukankan      html  css  js  c++  java
  • java并发:线程同步机制之计数器&Exechanger

    第一节 CountDownLatch

    (1)初识CountDownLatch

    (2)详述CountDownLatch

      CountDownLatch是通过一个计数器来实现的,计数器的初始值为线程的数量。每当一个线程完成了自己的任务后,计数器的值就会减1,当计数器值到达0时,它表示所有的线程已经完成了任务,然后在闭锁上等待的线程就可以恢复执行任务。

    CountDownLatch中主要方法如下:

      public CountDownLatch(int count),构造函数中的count(计数器)实际上就是闭锁需要等待的线程数量,这个值只能被设置一次,而且CountDownLatch没有提供任何机制去重新设置这个计数值。

       public void countDown(),每调用一次这个方法,在构造函数中初始化的count值就减1,通知机制是此方法来完成的。

       public void await() throws InterruptedException,调用此方法的当前线程会一直阻塞,直到计时器的值为0。

    (3)CountDownLatch示例

    package com.test;
    
    import java.util.concurrent.CountDownLatch;
    
    public class CountDownLatchDemo{
        
        public static void main(String args[]) throws Exception{
            CountDownLatch latch = new CountDownLatch(3);
            Worker worker1 = new Worker("Jack 程序员1",latch);
            Worker worker2 = new Worker("Rose 程序员2",latch);
            Worker worker3 = new Worker("Json 程序员3",latch);
            worker1.start();
            worker2.start();
            worker3.start();
            
            latch.await();
            System.out.println("Main thread end!");
        }
        
        static class Worker extends Thread {
            private String workerName;
            private CountDownLatch latch;
            public Worker(String workerName,CountDownLatch latch) {
                this.workerName = workerName;
                this.latch = latch;
            }
            @Override
            public void run() {
                try {
                    System.out.println("Worker:"+workerName +" is begin.");
                    Thread.sleep(1000L);
                    System.out.println("Worker:"+workerName +" is end.");
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }//模仿干活;
                latch.countDown();
            }
        }
    }

    上述程序运行结果如下:

    Worker:Rose 程序员2 is begin.
    Worker:Json 程序员3 is begin.
    Worker:Jack 程序员1 is begin.
    Worker:Jack 程序员1 is end.
    Worker:Json 程序员3 is end.
    Worker:Rose 程序员2 is end.
    Main thread end!

    从结果上可以看出,MainThread执行到latch.await();处会阻塞在该处,直到三个线程均完成的时候MainThread才会继续往下执行 

    (4)参考资料

    本小节只是简单描述了CountDownLatch的使用方式等,欲了解其实现机制,可以查看下面的几篇文章

    A、http://blog.itpub.net/30024515/viewspace-1432825/

    B、http://www.tuicool.com/articles/mQnAfq

    第二节 CyclicBarrier

    (1)初识CyclicBarrier

    (2)CyclicBarrier示例

    应用场景:在某种需求中,比如一个大型的任务,常常需要分配很多子任务去执行,只有当所有子任务都执行完成时候,才能执行主任务,这时候就可以选择CyclicBarrier了。

    示例:

    package com.test;
    import java.util.concurrent.BrokenBarrierException;
    import java.util.concurrent.CyclicBarrier;
    public class CyclicBarrierDemo{
        
        public static void main(String args[]) throws Exception{
            
            CyclicBarrier barrier = new CyclicBarrier(3,new TotalTask());
            
            BillTask worker1 = new BillTask("111",barrier);
            BillTask worker2 = new BillTask("222",barrier);
            BillTask worker3 = new BillTask("333",barrier);
            worker1.start();
            worker2.start();
            worker3.start();
            System.out.println("Main thread end!");
        }
        
        static class TotalTask extends Thread {
    public void run() {
                System.out.println("所有子任务都执行完了,就开始执行主任务了。");
            }
        }
        
        static class BillTask extends Thread {
            private String billName;
            private CyclicBarrier barrier;
            public BillTask(String workerName,CyclicBarrier barrier) {
                this.billName = workerName;
                this.barrier = barrier;
            }
            @Override
            public void run() {
                try {
                    System.out.println("市区:"+billName +"运算开始:");
                    Thread.sleep(1000L);//模仿第一次运算;
                    System.out.println("市区:"+billName +"运算完成,等待中...");
                    barrier.await();//假设一次运算不完,第二次要依赖第一次的运算结果。都到达这个节点之后后面才会继续执行;
                    System.out.println("全部都结束,市区"+billName +"才开始后面的工作。");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }
        }
        
    }

    上述程序运行结果如下:

    市区:111运算开始:
    市区:333运算开始:
    Main thread end!
    市区:222运算开始:
    市区:333运算完成,等待中...
    市区:222运算完成,等待中...
    市区:111运算完成,等待中...
    所有子任务都执行完了,就开始执行主任务了。//这句话是最后到达wait()方法的那个线程执行的
    全部都结束,市区111才开始后面的工作。
    全部都结束,市区222才开始后面的工作。
    全部都结束,市区333才开始后面的工作。

    解说:在这个示例中,构造CyclicBarrier时,传入了内部类TotalTask(TotalTask继承了Thread,是Runnable的实现)的实例对象,其意义在于:当所有的线程都执行到wait()方法时,它们会一起返回继续自己的工作,但是最后一个到达wait()方法的线程会执行TotalTask的run()方法;如果在构造构造CyclicBarrier时没有传入Runnable的实现对象作为构造参数,则当所有的线程都执行到wait()方法时会直接一起返回继续自己的工作。

    (3)CyclicBarrier与CountDownLatch的区别

    A、CountDownLatch的作用是允许1或N个线程等待其他线程完成执行;而CyclicBarrier则是允许N个线程相互等待;
    B、CountDownLatch的计数器无法被重置;而CyclicBarrier的计数器可以被重置后使用,因此它被称为是循环的barrier。

    第三节 Semaphore

    (1)初识Semaphore

      Java中的Semaphore用于在线程间传递信号,从概念上讲,信号量维护了一个许可集合,Semaphore只对可用的许可进行计数,并采取相应的行动。信号量常常用于多线程的代码中,比如数据库连接池。

    (2)Semaphore示例

    场景:假设一个服务器资源有限,任意某一时刻只允许3个人同时进行访问,这时一共来了10个人

    package com.test;
    import java.util.concurrent.Semaphore;
    public class SemaphoreDemo{
        
        public static void main(String args[]) throws Exception{
            
            final Semaphore semaphore = new Semaphore(3);//一次只运行3个人进行访问
            
            for(int i=0;i<10;i++) {
                final int no = i;
                Runnable thread = new Runnable() {
                    public void run (){
                        try {
                            System.out.println("用户"+no+"连接上了:");
                            Thread.sleep(300L);
                            semaphore.acquire();//获取接下去执行的许可
                            System.out.println("用户"+no+"开始访问后台程序...");
                            Thread.sleep(1000L);//模仿用户访问服务过程
                            semaphore.release();//释放允许下一个线程访问进入后台
                            System.out.println("用户"+no+"访问结束。");
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                };
                new Thread(thread).start();
            }
            
            System.out.println("Main thread end!");
        }
    }

    上述代码运行结果如下:

    用户1连接上了:
    用户3连接上了:
    用户4连接上了:
    用户2连接上了:
    用户0连接上了:
    用户5连接上了:
    用户7连接上了:
    Main thread end!
    用户6连接上了:
    用户8连接上了:
    用户9连接上了:
    用户3开始访问后台程序...
    用户4开始访问后台程序...
    用户2开始访问后台程序...
    用户4访问结束。
    用户3访问结束。
    用户7开始访问后台程序...
    用户0开始访问后台程序...
    用户8开始访问后台程序...
    用户2访问结束。
    用户5开始访问后台程序...
    用户0访问结束。
    用户7访问结束。
    用户1开始访问后台程序...
    用户8访问结束。
    用户6开始访问后台程序...
    用户1访问结束。
    用户9开始访问后台程序...
    用户5访问结束。
    用户6访问结束。
    用户9访问结束。

    从结果上可以看出来,10个人同时进来,但是只能同时3个人访问资源,释放一个允许进来一个

    (3)参考资料

    http://ifeve.com/semaphore/

    第四节 Exchanger

    (1)初识Exchanger

    此处的Exechanger与前面描述的几个同步机制不一样,前面描述的几个同步机制均是通过计数器来实现的,下面简单描述一下Exechanger,看看Exchanger的应用场景:

    注意:从上文描述,我们知道Exchanger用于在成对出现的线程之间(两个线程共有一个Exchanger)交换数据

    (2)Exechanger示例

                

    (3)参考资料

    http://www.cnblogs.com/davidwang456/p/4179488.html

  • 相关阅读:
    第七次作业
    随堂讨论8
    第六次作业
    随堂讨论-5
    曹宇轩-第五次作业
    随堂讨论3-刘昕昕,曹宇轩
    曹宇轩-第四次作业
    Alpha阶段项目复审
    团队作业4 -项目冲刺
    第六篇 Scrum冲刺博客
  • 原文地址:https://www.cnblogs.com/studyLog-share/p/5296449.html
Copyright © 2011-2022 走看看