旗下导航:搜·么
当前位置:网站首页 > JAVA教程 > 正文

Java中CountDownLatch、CyclicBarrier和Semaphore三个辅佐类的用法引见【JAVA教程】,Java中

作者:搜教程发布时间:2019-11-27分类:JAVA教程浏览:66评论:0


导读:本篇文章给人人带来的内容是关于Java中CountDownLatch、CyclicBarrier和Semaphore三个辅佐类的用法引见,有肯定的参考价值,有须要的朋侪可以参考一下...
本篇文章给人人带来的内容是关于Java中CountDownLatch、CyclicBarrier和Semaphore三个辅佐类的用法引见,有肯定的参考价值,有须要的朋侪可以参考一下,愿望对你有所协助。

在java 1.5中,供应了一些非常有效的辅佐类来协助我们举行并发编程,比方CountDownLatch,CyclicBarrier和Semaphore,本日我们就来进修一下这三个辅佐类的用法。

一.CountDownLatch用法

CountDownLatch类位于java.util.concurrent包下,应用它可以完成相似计数器的功用。比方有一个使命A,它要守候其他4个使命实行终了今后才实行,此时就可以应用CountDownLatch来完成这类功用了。

CountDownLatch类只供应了一个组织器:

public CountDownLatch(int count) {  };  //参数count为计数值

然后下面这3个要领是CountDownLatch类中最主要的要领:

public void await() throws InterruptedException { };   //挪用await()要领的线程会被挂起,它会守候直到count值为0才继承实行
public boolean await(long timeout, TimeUnit unit) throws InterruptedException { };  //和await()相似,只不过守候肯定的时候后count值还没变成0的话就会继承实行
public void countDown() { };  //将count值减1

下面看一个例子人人就清晰CountDownLatch的用法了:

public class Test {
     public static void main(String[] args) {  
         final CountDownLatch latch = new CountDownLatch(2);
 
         new Thread(){
             public void run() {
                 try {
                     System.out.println("子线程"+Thread.currentThread().getName()+"正在实行");
                    Thread.sleep(3000);
                    System.out.println("子线程"+Thread.currentThread().getName()+"实行终了");
                    latch.countDown();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
             };
         }.start();
 
         new Thread(){
             public void run() {
                 try {
                     System.out.println("子线程"+Thread.currentThread().getName()+"正在实行");
                     Thread.sleep(3000);
                     System.out.println("子线程"+Thread.currentThread().getName()+"实行终了");
                     latch.countDown();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
             };
         }.start();
 
         try {
             System.out.println("守候2个子线程实行终了...");
            latch.await();
            System.out.println("2个子线程已实行终了");
            System.out.println("继承实行主线程");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
     }
}

实行结果:

线程Thread-0正在实行
线程Thread-1正在实行
守候2个子线程实行终了...
线程Thread-0实行终了
线程Thread-1实行终了
2个子线程已实行终了
继承实行主线程

二.CyclicBarrier用法

字面意义回环栅栏,经由过程它可以完成让一组线程守候至某个状况今后再悉数同时实行。叫做回环是由于当一切守候线程都被开释今后,CyclicBarrier可以被重用。我们临时把这个状况就叫做barrier,当挪用await()要领今后,线程就处于barrier了。

CyclicBarrier类位于java.util.concurrent包下,CyclicBarrier供应2个组织器:

public CyclicBarrier(int parties, Runnable barrierAction) {
}
public CyclicBarrier(int parties) {
}

参数parties指让若干个线程或许使命守候至barrier状况;参数barrierAction为当这些线程都抵达barrier状况时会实行的内容。

然后CyclicBarrier中最主要的要领就是await要领,它有2个重载版本:

public int await() throws InterruptedException, BrokenBarrierException { };
public int await(long timeout, TimeUnit unit)throws InterruptedException,BrokenBarrierException,TimeoutException { };

第一个版本比较经常运用,用来挂起当前线程,直至一切线程都抵达barrier状况再同时实行后续使命;

第二个版本是让这些线程守候至肯定的时候,假如另有线程没有抵达barrier状况就直接让抵达barrier的线程实行后续使命。

下面举几个例子就邃晓了:

倘使有若干个线程都要举行写数据操纵,而且只要一切线程都完成写数据操纵今后,这些线程才继承做背面的事变,此时就可以应用CyclicBarrier了:

public class Test {
    public static void main(String[] args) {
        int N = 4;
        CyclicBarrier barrier  = new CyclicBarrier(N);
        for(int i=0;i<N;i++)
            new Writer(barrier).start();
    }
    static class Writer extends Thread{
        private CyclicBarrier cyclicBarrier;
        public Writer(CyclicBarrier cyclicBarrier) {
            this.cyclicBarrier = cyclicBarrier;
        }
 
        @Override
        public void run() {
            System.out.println("线程"+Thread.currentThread().getName()+"正在写入数据...");
            try {
                Thread.sleep(5000);      //以就寝来模仿写入数据操纵
                System.out.println("线程"+Thread.currentThread().getName()+"写入数据终了,守候其他线程写入终了");
                cyclicBarrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }catch(BrokenBarrierException e){
                e.printStackTrace();
            }
            System.out.println("一切线程写入终了,继承处置惩罚其他使命...");
        }
    }
}

实行结果:

线程Thread-0正在写入数据...
线程Thread-3正在写入数据...
线程Thread-2正在写入数据...
线程Thread-1正在写入数据...
线程Thread-2写入数据终了,守候其他线程写入终了
线程Thread-0写入数据终了,守候其他线程写入终了
线程Thread-3写入数据终了,守候其他线程写入终了
线程Thread-1写入数据终了,守候其他线程写入终了
一切线程写入终了,继承处置惩罚其他使命...
一切线程写入终了,继承处置惩罚其他使命...
一切线程写入终了,继承处置惩罚其他使命...
一切线程写入终了,继承处置惩罚其他使命...

从上面输出结果可以看出,每一个写入线程实行完写数据操纵今后,就在守候其他线程写入操纵终了。

当一切线程线程写入操纵终了今后,一切线程就继承举行后续的操纵了。

假如说想在一切线程写入操纵完今后,举行分外的其他操纵可以为CyclicBarrier供应Runnable参数:

public class Test {
    public static void main(String[] args) {
        int N = 4;
        CyclicBarrier barrier  = new CyclicBarrier(N,new Runnable() {
            @Override
            public void run() {
                System.out.println("当前线程"+Thread.currentThread().getName());  
            }
        });
 
        for(int i=0;i<N;i++)
            new Writer(barrier).start();
    }
    static class Writer extends Thread{
        private CyclicBarrier cyclicBarrier;
        public Writer(CyclicBarrier cyclicBarrier) {
            this.cyclicBarrier = cyclicBarrier;
        }
 
        @Override
        public void run() {
            System.out.println("线程"+Thread.currentThread().getName()+"正在写入数据...");
            try {
                Thread.sleep(5000);      //以就寝来模仿写入数据操纵
                System.out.println("线程"+Thread.currentThread().getName()+"写入数据终了,守候其他线程写入终了");
                cyclicBarrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }catch(BrokenBarrierException e){
                e.printStackTrace();
            }
            System.out.println("一切线程写入终了,继承处置惩罚其他使命...");
        }
    }
}

运转结果:

线程Thread-0正在写入数据...
线程Thread-1正在写入数据...
线程Thread-2正在写入数据...
线程Thread-3正在写入数据...
线程Thread-0写入数据终了,守候其他线程写入终了
线程Thread-1写入数据终了,守候其他线程写入终了
线程Thread-2写入数据终了,守候其他线程写入终了
线程Thread-3写入数据终了,守候其他线程写入终了
当前线程Thread-3
一切线程写入终了,继承处置惩罚其他使命...
一切线程写入终了,继承处置惩罚其他使命...
一切线程写入终了,继承处置惩罚其他使命...
一切线程写入终了,继承处置惩罚其他使命...

从结果可以看出,当四个线程都抵达barrier状况后,会从四个线程中挑选一个线程去实行Runnable。

下面看一下为await指定时候的结果:

public class Test {
    public static void main(String[] args) {
        int N = 4;
        CyclicBarrier barrier  = new CyclicBarrier(N);
 
        for(int i=0;i<N;i++) {
            if(i<N-1)
                new Writer(barrier).start();
            else {
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                new Writer(barrier).start();
            }
        }
    }
    static class Writer extends Thread{
        private CyclicBarrier cyclicBarrier;
        public Writer(CyclicBarrier cyclicBarrier) {
            this.cyclicBarrier = cyclicBarrier;
        }
 
        @Override
        public void run() {
            System.out.println("线程"+Thread.currentThread().getName()+"正在写入数据...");
            try {
                Thread.sleep(5000);      //以就寝来模仿写入数据操纵
                System.out.println("线程"+Thread.currentThread().getName()+"写入数据终了,守候其他线程写入终了");
                try {
                    cyclicBarrier.await(2000, TimeUnit.MILLISECONDS);
                } catch (TimeoutException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }catch(BrokenBarrierException e){
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"一切线程写入终了,继承处置惩罚其他使命...");
        }
    }
}

实行结果:

线程Thread-0正在写入数据...
线程Thread-2正在写入数据...
线程Thread-1正在写入数据...
线程Thread-2写入数据终了,守候其他线程写入终了
线程Thread-0写入数据终了,守候其他线程写入终了
线程Thread-1写入数据终了,守候其他线程写入终了
线程Thread-3正在写入数据...
java.util.concurrent.TimeoutException
Thread-1一切线程写入终了,继承处置惩罚其他使命...
Thread-0一切线程写入终了,继承处置惩罚其他使命...
    at java.util.concurrent.CyclicBarrier.dowait(Unknown Source)
    at java.util.concurrent.CyclicBarrier.await(Unknown Source)
    at com.cxh.test1.Test$Writer.run(Test.java:58)
java.util.concurrent.BrokenBarrierException
    at java.util.concurrent.CyclicBarrier.dowait(Unknown Source)
    at java.util.concurrent.CyclicBarrier.await(Unknown Source)
    at com.cxh.test1.Test$Writer.run(Test.java:58)
java.util.concurrent.BrokenBarrierException
    at java.util.concurrent.CyclicBarrier.dowait(Unknown Source)
    at java.util.concurrent.CyclicBarrier.await(Unknown Source)
    at com.cxh.test1.Test$Writer.run(Test.java:58)
Thread-2一切线程写入终了,继承处置惩罚其他使命...
java.util.concurrent.BrokenBarrierException
线程Thread-3写入数据终了,守候其他线程写入终了
    at java.util.concurrent.CyclicBarrier.dowait(Unknown Source)
    at java.util.concurrent.CyclicBarrier.await(Unknown Source)
    at com.cxh.test1.Test$Writer.run(Test.java:58)
Thread-3一切线程写入终了,继承处置惩罚其他使命...

上面的代码在main要领的for循环中,有意让末了一个线程启动耽误,由于在前面三个线程都抵达barrier今后,守候了指定的时候发明第四个线程还没有抵达barrier,就抛出非常并继承实行背面的使命。

别的CyclicBarrier是可以重用的,看下面这个例子:

public class Test {
    public static void main(String[] args) {
        int N = 4;
        CyclicBarrier barrier  = new CyclicBarrier(N);
 
        for(int i=0;i<N;i++) {
            new Writer(barrier).start();
        }
 
        try {
            Thread.sleep(25000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
 
        System.out.println("CyclicBarrier重用");
 
        for(int i=0;i<N;i++) {
            new Writer(barrier).start();
        }
    }
    static class Writer extends Thread{
        private CyclicBarrier cyclicBarrier;
        public Writer(CyclicBarrier cyclicBarrier) {
            this.cyclicBarrier = cyclicBarrier;
        }
 
        @Override
        public void run() {
            System.out.println("线程"+Thread.currentThread().getName()+"正在写入数据...");
            try {
                Thread.sleep(5000);      //以就寝来模仿写入数据操纵
                System.out.println("线程"+Thread.currentThread().getName()+"写入数据终了,守候其他线程写入终了");
 
                cyclicBarrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }catch(BrokenBarrierException e){
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"一切线程写入终了,继承处置惩罚其他使命...");
        }
    }
}

实行结果:

线程Thread-0正在写入数据...
线程Thread-1正在写入数据...
线程Thread-3正在写入数据...
线程Thread-2正在写入数据...
线程Thread-1写入数据终了,守候其他线程写入终了
线程Thread-3写入数据终了,守候其他线程写入终了
线程Thread-2写入数据终了,守候其他线程写入终了
线程Thread-0写入数据终了,守候其他线程写入终了
Thread-0一切线程写入终了,继承处置惩罚其他使命...
Thread-3一切线程写入终了,继承处置惩罚其他使命...
Thread-1一切线程写入终了,继承处置惩罚其他使命...
Thread-2一切线程写入终了,继承处置惩罚其他使命...
CyclicBarrier重用
线程Thread-4正在写入数据...
线程Thread-5正在写入数据...
线程Thread-6正在写入数据...
线程Thread-7正在写入数据...
线程Thread-7写入数据终了,守候其他线程写入终了
线程Thread-5写入数据终了,守候其他线程写入终了
线程Thread-6写入数据终了,守候其他线程写入终了
线程Thread-4写入数据终了,守候其他线程写入终了
Thread-4一切线程写入终了,继承处置惩罚其他使命...
Thread-5一切线程写入终了,继承处置惩罚其他使命...
Thread-6一切线程写入终了,继承处置惩罚其他使命...
Thread-7一切线程写入终了,继承处置惩罚其他使命...

从实行结果可以看出,在首次的4个线程超出barrier状况后,又可以用来举行新一轮的运用。而CountDownLatch没法举行重复运用。

三.Semaphore用法

Semaphore翻译成字面意义为 信号量,Semaphore可以控同时接见的线程个数,经由过程 acquire() 猎取一个许可,假如没有就守候,而 release() 开释一个许可。

Semaphore类位于java.util.concurrent包下,它供应了2个组织器:

public Semaphore(int permits) {          //参数permits示意许可数量,即同时可以许可若干线程举行接见
    sync = new NonfairSync(permits);
}
public Semaphore(int permits, boolean fair) {    //这个多了一个参数fair示意是不是是平正的,即守候时候越久的越先猎取许可
    sync = (fair)? new FairSync(permits) : new NonfairSync(permits);
}

下面说一下Semaphore类中比较主要的几个要领,首先是acquire()、release()要领:

public void acquire() throws InterruptedException {  }     //猎取一个许可
public void acquire(int permits) throws InterruptedException { }    //猎取permits个许可
public void release() { }          //开释一个许可
public void release(int permits) { }    //开释permits个许可

acquire()用来猎取一个许可,若无许可可以取得,则会一向守候,直到取得许可。

release()用来开释许可。注重,在开释许可之前,必须先获取得许可。

这4个要领都会被壅塞,假如想马上获得实行结果,可以运用下面几个要领:

public boolean tryAcquire() { };    //尝试猎取一个许可,若猎取胜利,则马上返回true,若猎取失利,则马上返回false
public boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException { };  //尝试猎取一个许可,若在指定的时候内猎取胜利,则马上返回true,不然则马上返回false
public boolean tryAcquire(int permits) { }; //尝试猎取permits个许可,若猎取胜利,则马上返回true,若猎取失利,则马上返回false
public boolean tryAcquire(int permits, long timeout, TimeUnit unit) throws InterruptedException { }; //尝试猎取permits个许可,若在指定的时候内猎取胜利,则马上返回true,不然则马上返回false

别的还可以经由过程availablePermits()要领获得可用的许可数量。

下面经由过程一个例子来看一下Semaphore的细致运用:

倘使一个工场有5台机械,但是有8个工人,一台机械同时只能被一个工人运用,只要运用完了,其他工人才继承运用。那末我们就可以经由过程Semaphore来完成:

public class Test {
    public static void main(String[] args) {
        int N = 8;            //工人数
        Semaphore semaphore = new Semaphore(5); //机械数量
        for(int i=0;i<N;i++)
            new Worker(i,semaphore).start();
    }
 
    static class Worker extends Thread{
        private int num;
        private Semaphore semaphore;
        public Worker(int num,Semaphore semaphore){
            this.num = num;
            this.semaphore = semaphore;
        }
 
        @Override
        public void run() {
            try {
                semaphore.acquire();
                System.out.println("工人"+this.num+"占用一个机械在生产...");
                Thread.sleep(2000);
                System.out.println("工人"+this.num+"开释出机械");
                semaphore.release();          
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

实行结果:

工人0占用一个机械在生产...
工人1占用一个机械在生产...
工人2占用一个机械在生产...
工人4占用一个机械在生产...
工人5占用一个机械在生产...
工人0开释出机械
工人2开释出机械
工人3占用一个机械在生产...
工人7占用一个机械在生产...
工人4开释出机械
工人5开释出机械
工人1开释出机械
工人6占用一个机械在生产...
工人3开释出机械
工人7开释出机械
工人6开释出机械

下面对上面说的三个辅佐类举行一个总结:

1)CountDownLatch和CyclicBarrier都可以完成线程之间的守候,只不过它们侧重点差别:

CountDownLatch平常用于某个线程A守候若干个其他线程实行完使命今后,它才实行;

而CyclicBarrier平常用于一组线程相互守候至某个状况,然后这一组线程再同时实行;

别的,CountDownLatch是不可以重用的,而CyclicBarrier是可以重用的。

2)Semaphore实在和锁有点相似,它平常用于掌握对某组资本的接见权限。

以上就是Java中CountDownLatch、CyclicBarrier和Semaphore三个辅佐类的用法引见的细致内容,更多请关注ki4网别的相干文章!

标签:Java中


欢迎 发表评论: