Java中的并发工具

1. 等待多线程完成CountDownLatch

当子线程需要等到子线程执行完成才继续执行可以使用CountDownLatch:

public class CountDownLatchTest {

    /**
     * 等待其他线程执行完成主线程再继续执行 CountDownLatch
     * c.countDown();
     * c.await();
     *
     * @throws InterruptedException 中断异常
     */
    @Test
    public void test() throws InterruptedException {
        CountDownLatch c = new CountDownLatch(2);
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(1);
                c.countDown();
                System.out.println(2);
                c.countDown();
            }
        }).start();
        c.await();
        System.out.println("3");
    }
}

CountDownLatch的构造函数接收一个int类型的参数作为计数器,如果你想等待N个点完 传入N。 当我们调用countDown方法时,N就会减1,await方法 会阻塞当前线程,直到N变成零。由于countDown方法可以用在任何地方,所以这里说的N个点,可以是N个线程,也可以是1个线程里的N个执行步骤。用在多个线程时,只需要把这个 CountDownLatch的引用传递到线程里即可。

2. 同步屏障CyclicBarrier

让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,所有被屏障拦截的线程才会继续运行。

/**
 * 同步屏障
 * 如果把new CyclicBarrier(2)修改成new CyclicBarrier(3),则主线程和子线程会永远等待,
 * 因为没有第三个线程执行await方法,即没有第三个线程到达屏障,所以之前到达屏障的两个 线程都不会继续执行。
 *
 * @author liulu40
 **/
public class CyclicBarrierShow {
    static CyclicBarrier c = new CyclicBarrier(2);

    public static void main(String[] args) throws Exception {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    c.await();
                } catch (Exception e) {
                }
                System.out.println(1);
            }
        }).start();
        c.await();
        System.out.println(2);
    }
}

CyclicBarrier还提供一个更高级的构造函数CyclicBarrier(int parties,Runnable barrier- Action),用于在线程到达屏障时,优先执行barrierAction,方便处理更复杂的业务场景

CyclicBarrier的应用场景:
CyclicBarrier可以用于合并计算结果的场景。例如统计银行流水,分别计算4组的流水最后汇总

/**
 * 银行流水处理服务类
 *
 * @author liulu40
 **/
public class BankWaterService implements Runnable {
    /**
     * 创建4个屏障,处理完之后执行当前类的run方法
     */
    private CyclicBarrier c = new CyclicBarrier(4, this);
    /**
     * 假设只有4个sheet,所以只启动4个线程
     */
    private Executor executor = Executors.newFixedThreadPool(4);
    /**
     * 保存每个sheet计算出的银流结果
     */
    private ConcurrentHashMap<String, Integer> sheetBankWaterCount = new ConcurrentHashMap<>();

    private void count() {
        for (int i = 0; i < 4; i++) {
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    // 计算当前sheet的银流数据,计算代码省略
                    sheetBankWaterCount.put(Thread.currentThread().getName(), 1);
                    try {
                        // 银流计算完成,插入一个屏障
                        c.await();
                    } catch (InterruptedException |
                            BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                }
            });
        }

    }

    @Override
    public void run() {
        // 汇总每个sheet计算出的结果
        int result = 0;
        for (Entry<String, Integer> sheet : sheetBankWaterCount.entrySet()) {
            result += sheet.getValue();
        }
        // 将结果输出
        sheetBankWaterCount.put("result", result);
        System.out.println(result);
    }

    public static void main(String[] args) {
        BankWaterService bankWaterCount = new BankWaterService();
        bankWaterCount.count();
    }
}

3.控制并发线程数的Semaphore

Semaphore可以用于做流量控制

/**
 * @author alu4r
 **/
public class SemaphoreTest {
    private static final int THREAD_COUNT = 30;
    private static ExecutorService threadPool = Executors.newFixedThreadPool(THREAD_COUNT);
    private static Semaphore s = new Semaphore(10);

    public static void main(String[] args) {
        for (int i = 0; i < THREAD_COUNT; i++) {
            threadPool.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        s.acquire();
                        System.out.println("save data");
                        s.release();
                    } catch (InterruptedException e) {
                    }
                }
            });
        }
        threadPool.shutdown();
    }
}

虽然有30个线程在执行,但是只允许10个并发执行。Semaphore的构造方法 Semaphore(10)表示允许10个线程获取许可证,也就是最大并发数是10。首先线程使用 Semaphore的acquire()方法获取一个许可证,使用完之后调用release()方法归还许可证

  • int availablePermits():返回此信号量中当前可用的许可证数。
  • int getQueueLength():返回正在等待获取许可证的线程数
  • boolean hasQueuedThreads():是否有线程正在等待获取许可证
  • void reducePermits(int reduction):减少reduction个许可证,是个protected方法
  • Collection getQueuedThreads():返回所有等待获取许可证的线程集合,是个protected方 法

4.线程间交换数据的Exchanger

Exchanger用于进行线程间的数据交换。提供一个同步点,在这个同步点,两个线程可以交换彼此的数据。这两个线程通过exchange方法交换数据,如果第一个线程先执行exchange()方法,它会一直等待第二个线程也 执行exchange方法,当两个线程都到达同步点时,这两个线程就可以交换数据,将本线程生产出来的数据传递给对方。

/**
 * 数据交换
 *
 * @author alu4r
 **/
public class ExchangerTest {
    private static final Exchanger<String> exgr = new Exchanger<>();
    private static ExecutorService threadPool = Executors.newFixedThreadPool(2);

    public static void main(String[] args) {
        threadPool.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    // A录入银行流水数据
                    String A = "银行流水A";
                    String B = exgr.exchange(A);
                    System.out.println("A线程收到数据:" + B);
                } catch (InterruptedException e) {
                }
            }
        });
        threadPool.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    // B录入银行流水数据
                    String B = "银行流水B";
                    String A = exgr.exchange(B);
                    System.out.println("B线程收到数据:" + A);
                } catch (InterruptedException e) {
                }
            }
        });
        threadPool.shutdown();
    }
}

注意:两个线程之间的交换,当线程C第三次执行exchange方法时候,Exchanger会继续等待第4次执行exchange的方法

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值