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

  • 相关阅读:
    POJ 1015 Jury Compromise【DP】
    POJ 1661 Help Jimmy【DP】
    HDU 1074 Doing Homework【状态压缩DP】
    HDU 1024 Max Sum Plus Plus【DP,最大m子段和】
    占坑补题。。最近占的坑有点多。。。
    Codeforces 659F Polycarp and Hay【BFS】
    Codeforces 659E New Reform【DFS】
    Codeforces 659D Bicycle Race【计算几何】
    廖大python实战项目第四天
    廖大python实战项目第三天
  • 原文地址:https://www.cnblogs.com/studyLog-share/p/5296449.html
Copyright © 2011-2022 走看看