1、什么是多线程同步器?

可以理解为,用于控制多线程之前同步动作的工具。

2、为什么使用多线程同步器?

在实际应用中,我们希望多线程根据某些一些特定的规则执行。因此有了多线程同步器,通过不同的多线程同步器,可以让多线程实现多样的行为。

3、多线程同步器介绍

3.1、Samaphore

应用场景:对于一组有限制都资源访问。比如餐厅有2个位置但同时有10个人要吃饭,则要控制10个人对餐位的并发实用。即当餐位有空余时,就会有人用餐,没有空余时,则进入阻塞等待。

用法:定义Semaphore变量semaphore包含受限的资源个数,每个人要来用餐时先调用semaphore.acquire()方法获取一个餐位(若没有餐位,则阻塞等待),用完餐后调用semaphore.release()释放餐位给其它人用。

package study.threadPoolTest;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

public class SamaphoreTest {
    private static Semaphore semaphore = new Semaphore(2);

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(10);

        for (int i = 0; i < 10; i++) {
            Person p = new Person(i);
            executorService.submit(p);
        }
        executorService.shutdown();

    }

    static class Person implements Callable {

        int i;

        Person(int i) {
            this.i = i;
        }

        @Override
        public Object call() throws Exception {
            semaphore.acquire();
            System.out.println("start:" + i);
            Thread.sleep(2000);
            System.out.println("finished:" + i);
            semaphore.release();
            return null;
        }
    }
}

结果:


semaphore的值为2,即最多同时只有2个线程在执行。
start:0
start:1
两个线程执行。
当且仅当
finished:0
第0号线程结束了,第2号线程才开始
start:2


start:0
start:1
finished:0
start:2
finished:1
start:3
finished:2
start:4
finished:3
start:5
finished:4
start:6
finished:5
start:7
finished:6
start:8
finished:7
start:9
finished:8
finished:9

3.2、CountDownLatch

应用场景:等待一组线程任务完成后在继续执行当前线程。

用法:定义一个CountDownLatch变量latch,在当前线程中调用latch.await()方法,在要等待的一组线程中执行完后调用latch.countDown()方法,这样当该做线程都调用过latch.countDown()方法后就开始执行当前线程latch.await()后的方法。

package study.threadPoolTest;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CountDownLatchTest {

    private static CountDownLatch countDownLatch = new CountDownLatch(2);
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(10);

        executorService.submit(new mainThread());
        for (int i = 0;i<5;i++){
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                System.out.println("Exception" + e);
            }
            int j = i;
            executorService.submit(() -> {
                System.out.println("start" + j);
                countDownLatch.countDown();
                System.out.println("finished " + j);

            });
        }
        executorService.shutdown();
    }
    static class mainThread implements Runnable{

        @Override
        public void run() {
            try {
                System.out.println("start");
                countDownLatch.await();
                System.out.println("finished");
            } catch (InterruptedException e) {
                System.out.println("Exception" + e);
            }
        }
    }

}

结果:


countDownLatch的值为2,表示,要等两个线程执行了之后,才继续执行。
start
主线程start之后,等在countDownLatch.await();
start0
finished 0
start1
finished 1
当两个线程执行了,主线程继续执行
finished


start
start0
finished 0
start1
finished 1
finished
start2
finished 2
start3
finished 3
start4
finished 4

3.3、CyclierBarrier

应用场景:等待一组线程到达某个点后一起执行,该组线程达到指定点后可以再次循环执行。也可用于一组线程达达某个点后再执行某个方法。

用法:定义一个CyclicBarrier变量barrier,线程达到某个约定点时调用barrier.await()方法,当该组所有线程都调用了barrier.await()方法后改组线程一起向下执行。

CyclicBarrier和CountDownLatch的区别

CountDownLatch的计数器只能使用一次。而CyclicBarrier的计数器可以使用reset() 方法重置。所以CyclicBarrier能处理更为复杂的业务场景,比如如果计算发生错误,可以重置计数器,并让线程们重新执行一次。
CyclicBarrier还提供其他有用的方法,比如getNumberWaiting方法可以获得CyclicBarrier阻塞的线程数量。isBroken方法用来知道阻塞的线程是否被中断。

package study.threadPoolTest;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CyclicBarrierTest {

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
        for (int i = 0;i < 10;i++){
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            int finalI = i;
            executorService.submit(() -> {
                try {
                    System.out.println("start" + finalI + "count=" + cyclicBarrier.getNumberWaiting());
                    cyclicBarrier.await();
                    System.out.println("finished" + finalI);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }

            });
        }
        executorService.shutdown();
    }
}

结果:


CyclierBarrier的值为3,表示每次都会拦截线程,拦截到3个线程,才继续执行。且CyclierBarrier一直有效
start3count=0
start4count=1
start5count=2
finished5
finished3
finished4
start6count=0
start7count=1
start8count=2
finished8
finished6
finished7
start9count=0


start0count=0
start1count=1
start2count=2
finished2
finished0
finished1
start3count=0
start4count=1
start5count=2
finished5
finished3
finished4
start6count=0
start7count=1
start8count=2
finished8
finished6
finished7
start9count=0

 

更多内容请关注微信公众号“外里科技

官方公众号

外里科技

运营公众号

英雄赚





开源代码

https://gitee.com/B_T/beimi

java多线程 —— 多线程同步器_并发编程