Java并发包 AQS

参考https://github.com/CyC2018/Interview-Notebook/blob/master/notes/Java%20%E5%B9%B6%E5%8F%91.md#%E4%B8%83juc—aqs

CountdownLatch 闭锁
用来控制一个线程等待多个线程。
维护了一个计数器 cnt,每次调用 countDown() 方法会让计数器的值减 1,减到 0 的时候,那些因为调用 await() 方法而在等待的线程就会被唤醒。
代码例子参考如下博客http://www.cnblogs.com/cz123/p/7503545.html
设想这样的场景,有一个线程要统计结果,结果是由四个线程分别的处理结果汇总起来的。这时候统计结果线程需要等四个处理线程结束才可以开始统计。实现这一点要靠CountdownLatch,在统计结果的线程中执行CountdownLatch.await()方法,让统计线程进入等待队列。等到每个处理数据线程都执行完后,CountdowLatch.countDown()方法,等计数器减到0的时候,唤醒调用await()方法而等待的线程。

CountDownLatch是通过“共享锁”实现的,在创建CountDownLatch时,会传递一个int类型参数,该参数是“锁计数器”的初始状态,表示该“共享锁”最多能被count个线程同时获取,这个值只能被设置一次。主线程必须在启动其他线程后立即调用await()方法。这样主线程的操作就会在这个方法上阻塞,知道其他线程完成各自的任务。当某线程调用CountDownLatch对象的await()方法时,该线程会等待共享锁可用时,才会获取共享锁二继续运行。共享锁的可用条件,就是锁计数器的值为0。每当一个线程调用该CountDoenLatch对象的countDown()方法时,才将“锁计数器”-1。

package countDownLatch;

import java.util.concurrent.CountDownLatch;

/**
 * Created by on 2018/6/8.
 * http://www.cnblogs.com/cz123/p/7503545.html
 */
public class CountDownLatchTest {
    public static void main(String[] args) {
        CountDownLatch latch = new CountDownLatch(4);

        new Thread() {
            @Override
            public void run() {
                System.out.println("加工线程:" + Thread.currentThread().getName() + "开始等待数据");
                try {
                    latch.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("数据都到齐了!!!");
            }
        }.start();

        for (int i=0; i<4; i++) {
            new Thread() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "开始统计数据");
                    try {
                        sleep(1000);  // 模拟统计过程
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "统计完成");
                    latch.countDown();
                }
            }.start();
        }
    }
}

上面的代码也可以这么写,效果是一样的。来源于参考链接中的GitHub上的例子。

package countDownLatch;

import java.util.concurrent.CountDownLatch;

/**
 * Created by on 2018/6/8.
 * http://www.cnblogs.com/cz123/p/7503545.html
 */
public class CountDownLatchTest {
    public static void main(String[] args) {
        CountDownLatch latch = new CountDownLatch(4);

        new Thread() {
            @Override
            public void run() {
                System.out.println("加工线程:" + Thread.currentThread().getName() + "开始等待数据");
                for (int i=0; i<4; i++) {
                    new Thread() {
                        @Override
                        public void run() {
                            System.out.println(Thread.currentThread().getName() + "开始统计数据");
                            try {
                                sleep(1000);  // 模拟统计过程
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                            System.out.println(Thread.currentThread().getName() + "统计完成");
                            latch.countDown();
                        }
                    }.start();
                }
                try {
                    System.out.println("运行到await()");
                    latch.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("数据都到齐了!!!");
            }
        }.start();


    }
}

运行结果

加工线程:Thread-0开始等待数据
Thread-1开始统计数据
Thread-2开始统计数据
Thread-3开始统计数据
Thread-4开始统计数据
Thread-1统计完成
Thread-3统计完成
Thread-2统计完成
Thread-4统计完成
数据都到齐了!!!
package countDownLatch;

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

/**
 * Created by Administrator on 2018/7/1 0001.
 */
public class CountdownLatchExample {
    public static void main(String[] args) throws InterruptedException {
        final int totalThread = 4;
        CountDownLatch countDownLatch = new CountDownLatch(totalThread);
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < totalThread; i++) {
            executorService.execute(() -> {
                System.out.println("run");
                countDownLatch.countDown();
            });
        }
        countDownLatch.await();
        System.out.println("end");
        executorService.shutdown();
    }
}

2、CyclicBarrier 循环屏障
CountDownLatch和CyclicBarrier的区别
(1)CountDownLatch的作用是允许1个线程等待其他线程执行完成后,它才执行。二CyclicBarrier允许N个线程相互等待到某个公共屏障点,然后一组线程再同时执行。
(2)CountDownLatch的计数器的值无法被重置,这个初始值只能被设置一次,是不能够重用的。CyclicBarrier可以重用。

package countDownLatch;

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

/**
 * Created by on 2018/6/8.
 */

public class CyclicBarrierTest {
    public static void main(String[] args) {
        CyclicBarrier barrier = new CyclicBarrier(4);

        for (int i=0; i<4; i++) {
            new Thread() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "开始检查文件");
                    try {
                        sleep(2000);  // 模拟检查过程
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "检查文件结束,等待其他线程");
                    try {
                        barrier.await();  // 等待
                    } catch (InterruptedException | BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "统计");
                }
            }.start();
        }
    }
}
Thread-3开始检查文件
Thread-1开始检查文件
Thread-2开始检查文件
Thread-0开始检查文件
Thread-0检查文件结束,等待其他线程
Thread-2检查文件结束,等待其他线程
Thread-3检查文件结束,等待其他线程
Thread-1检查文件结束,等待其他线程
Thread-1统计
Thread-3统计
Thread-0统计
Thread-2统计

3、Semaphore 信号量
可以控制某个资源可悲同时访问的个数,通过构造函数设定一定数量的许可,通过acquire()获取一个许可,如果没有就等待,二release()释放一个许可。

package AQS;

import java.util.concurrent.Semaphore;

/**
 * Created by Administrator on 2018/7/20 0020.
 */
public class SemaphoreExample {
    public static void main(String[] args) {
        Semaphore windows = new Semaphore(5);  // 声明5个窗口

        for (int i = 0; i < 8; i++) {
            new Thread() {
                @Override
                public void run() {
                    try {
                        windows.acquire();  // 占用窗口
                        System.out.println(Thread.currentThread().getName() + ": 开始买票");
                        sleep(2000);  // 睡2秒,模拟买票流程
                        System.out.println(Thread.currentThread().getName() + ": 购票成功");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        windows.release();  // 释放窗口
                    }
                }
            }.start();
        }
    }
}
Thread-2: 开始买票
Thread-0: 开始买票
Thread-1: 开始买票
Thread-3: 开始买票
Thread-4: 开始买票
Thread-2: 购票成功
Thread-3: 购票成功
Thread-0: 购票成功
Thread-1: 购票成功
Thread-5: 开始买票
Thread-6: 开始买票
Thread-7: 开始买票
Thread-4: 购票成功
Thread-6: 购票成功
Thread-5: 购票成功
Thread-7: 购票成功

猜你喜欢

转载自blog.csdn.net/qq_31617121/article/details/80878033