JUC ⼯具类-CountDownLatch、CyclicBarrier、Semaphore、读写锁

在我前面的博客里已经写过了一些JUC工具类,比如Lock的RentrantLock,可以防止并发访问异常(IllegalMonitorStateException)的CopyOnWriteArrayList、CopyOnWriteArraySet、ConcurrentHashMap。


本篇博客继续记录JUC工具类中常用的一些类。

1. CountDownLatch:减法计数器

可以⽤来倒计时,当两个线程同时执⾏时,如果要确保⼀个线程优先执⾏,可以使⽤计数器,当计数器
清零的时候,再让另⼀个线程执⾏。

public class CountDownLatchTest {
    public static void main(String[] args) {
        //JUC包提供的减法计数器
        CountDownLatch countDownLatch = new CountDownLatch(100);

        new Thread(()->{
            for (int i = 0; i < 100 ;i++){
                System.out.println(i);
                countDownLatch.countDown();
            }
        }).start();

        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        for (int i = 0; i < 30 ;i++){
            System.out.println("==========main----------");
        }

    }
}

coutDown():计数器减⼀
await():计数器停⽌,唤醒其他线程
new CountDownLatch(100)、coutDown()、await() 必须配合起来使⽤,创建对象的时候赋的值是多少,coutDown() 就必须执⾏多少次,否则计数器是没有清零的,计数器就不会停⽌,其他线程也⽆法唤醒,所以必须保证计数器清零,coutDown() 的调⽤次数必须⼤于构造函数的参数值。

2. CyclicBarrier:加法计数器
public class CyclicBarrierTest {
    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(100,()->{
            System.out.println("放行");
        });

        for (int i=0; i<109; i++){
            int finalI = i;
            new Thread(()->{
                System.out.println("==>"+ finalI);
                try {
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }

            }).start();
        }
    }
}

await():在其他线程中试图唤醒计数器线程,当其他线程的执⾏次数达到计数器的临界值时,则唤醒计数器线程,并且计数器是可以重复使⽤的,当计数器的线程执⾏完成⼀次之后,计数器⾃动清零,等待下⼀次执⾏。
new CyclicBarrier(30),for 执⾏ 90 次,则计数器的任务会执⾏ 3 次。

3. Semaphore:计数信号量

实际开发中主要使⽤它来完成限流操作,限制可以访问某些资源的线程数量。
Semaphore 只有 3 个操作:
(1)初始化
(2)获取许可
(3)释放

public class SemaphoreTest {
    public static void main(String[] args) {
        //初始化
        Semaphore semaphore = new Semaphore(5);
        for (int i = 0; i < 15; i++) {
            new Thread(()->{
                //获得许可
                try {
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName()+"进店购物");
                            TimeUnit.SECONDS.sleep(5);
                    System.out.println(Thread.currentThread().getName()+"出店");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    //释放
                    semaphore.release();
                }
            },String.valueOf(i)).start();
        }
    }
}

每个线程在执⾏的时候,⾸先需要去获取信号量,只有获取到资源才可以执⾏,执⾏完毕之后需要释放资源,留给下⼀个线程。

4.读写锁

接⼝ ReadWriteLock,实现类是 ReentrantReadWriteLock,可以多线程同时读,但是同⼀时间内只能有⼀个线程进⾏写⼊操作。

读写锁也是为了实现线程同步,只不过粒度更细,可以分别给读和写的操作设置不同的锁。

public class readWriteLockTest {
    public static void main(String[] args) {
        Cache cache = new Cache();
        for (int i=0;i<10;i++){
            final int temp = i;
            new Thread(()->{
                cache.write(temp,String.valueOf(temp));
            }).start();
        }

        for (int i=0;i<10;i++){
            final int temp = i;
            new Thread(()->{
                cache.read(temp);
            }).start();
        }
    }
}

class Cache{
    private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    private Map<Integer,String> map = new HashMap<Integer,String>();


    /**
     * 写操作
     */
    public void write(Integer key,String val){
        readWriteLock.writeLock().lock();
        System.out.println("开始写入"+ key);
        map.put(key,val);
        System.out.println("写入完毕"+ key);
        readWriteLock.writeLock().unlock();
    }

    /**
     * 读操作
     */
    public void read(Integer key){
        readWriteLock.readLock().lock();
        System.out.println("开始读取"+ key);
        map.get(key);
        System.out.println("读取完毕"+ key);
        readWriteLock.readLock().unlock();
    }
}

执行结果:

开始写入0
写入完毕0
开始写入7
写入完毕7
开始写入1
写入完毕1
开始写入2
写入完毕2
开始写入4
写入完毕4
开始写入3
写入完毕3
开始写入9
写入完毕9
开始写入6
写入完毕6
开始写入5
写入完毕5
开始写入8
写入完毕8
开始读取8
开始读取5
开始读取0
读取完毕0
读取完毕8
开始读取1
开始读取4
开始读取2
读取完毕2
开始读取3
读取完毕5
读取完毕3
开始读取9
读取完毕9
开始读取6
读取完毕4
读取完毕1
开始读取7
读取完毕6
读取完毕7

很明显,写入的时候是是能一个线程单独执行,读取的时候多线程同时读取。

写⼊锁也叫独占锁,只能被⼀个线程占⽤,读取锁也叫共享锁,多个线程可以同时占⽤。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章