Java并发编程-精通篇(工具类)

常用的Concurrent同步工具类
CountDownLatch
CyclicBarrier
Semaphore
Exchanger
ReentrantLock
ReentrantReadWriteLock

1.CountDownLatch

线程安全计数器。允许一个或多个线程等待一系列的操作完成。
给一个指定数值初始化,然后通过countDown()方法将计数器减一。
通过await()方法,线程可以阻塞等待直到计数器为零。
由于用法比较简单,就不写DEMO了(懒)

2.CyclicBarrier

循环栅栏,允许一组线程相互等待,直到达到某个公共屏障点(common barrier point)。
在涉及固定大小的线程的程序中,这些线程必须不是的相互等待,这时使用CyclicBarrier可以使等待线程达到common barrier point时得到释放。

使用场景:需要所有子任务都完成时,才执行主任务。在使用时,工作线程数一定要大于CyclicBarrier初始化的数值,否则将永远阻塞下去。

package com.jimmy.concurrent.util;

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

public class CyclicBarrierDemo {

    static class Runner implements Runnable {

        private CyclicBarrier barrier;
        private String name;

        public Runner(CyclicBarrier barrier, String name) {
            this.barrier = barrier;
            this.name = name;
        }

        @Override
        public void run() {
            try {
                Thread.sleep((new Random().nextInt(8)) * 1000 );
                System.out.println(name + " is ready.[" + System.currentTimeMillis()  + ']');
                // 所有参与者都在此执行await之前,将一直阻塞
                barrier.await();
                // 所有参与者的await方法都执行后,将同时执行下面动作
                System.out.println(name + " run! [" + System.currentTimeMillis()  + "]");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }

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

        ExecutorService executor = Executors.newFixedThreadPool(3);
        executor.submit(new Thread(new Runner(barrier, "No1 Runner")));
        executor.submit(new Thread(new Runner(barrier, "No2 Runner")));
        executor.submit(new Thread(new Runner(barrier, "No3 Runner")));

        executor.shutdown();
    }
}

执行结果:从输出run的执行时间可以看出,当所有的await()方法执行完成之后,后序的动作确实是同时执行的。
在这里插入图片描述

3. Semaphore

计数信号量,信号量维护了一个许可集合。
通过acquire()来获取一个许可,只有成功获取了许可,线程才能继续执行。如果无法获取到许可,线程将阻塞。
通过release()来是放一个许可,只有成功释放了许可,其他线程才能去获取新的许可。

DEMO

package com.jimmy.concurrent.util;

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

public class SemaphoreDemo {

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

        final Semaphore semaphore = new Semaphore(2);

        for (int i = 0 ; i < 5; i ++) {
            Runnable run = () -> {
                try {
                    System.out.println(Thread.currentThread() + "try to require permit");
                    // 获取许可
                    semaphore.acquire();
                    System.out.println(Thread.currentThread() + "require permit.");
                    Thread.sleep(1000);
                    System.out.println(Thread.currentThread() + "release permit");
                    // 释放许可,如果不释放,所有未获取到许可的线程将被阻塞
                    semaphore.release();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            };
            executorService.submit(run);
        }
        executorService.shutdown();
    }
}

正常释放许可的结果:每次有两个线程获取到许可并继续执行。
在这里插入图片描述
未正常释放许可的结果:许可耗尽之后,其他线程阻塞
在这里插入图片描述

4. Exchanger

交换器,可以在两个线程之间互换对象的汇合点。
必须是两个线程,且都已达到汇合点,才能进行数据交换

package com.jimmy.concurrent.util;

import java.util.Random;
import java.util.concurrent.Exchanger;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ExchangerDemo {

    public static void main(String[] args) {
        Exchanger<String> exchanger = new Exchanger<>();
        ExecutorService executor = Executors.newCachedThreadPool();

        executor.submit(new ExchangerRunner(exchanger, "A"));
        executor.submit(new ExchangerRunner(exchanger, "B"));

        executor.shutdown();
    }
}

class ExchangerRunner implements Runnable {

    private Exchanger<String> exchanger;
    private String data;

    public ExchangerRunner(Exchanger<String> exchanger, String data) {
        this.exchanger = exchanger;
        this.data = data;
    }

    @Override
    public void run() {
        try {
            System.out.println(Thread.currentThread() + " data before exchange " + this.data);
            Thread.sleep(new Random().nextInt(5) * 1000);
            // 记录达到汇合点的时间
            System.out.println(Thread.currentThread() + " is wait for exchange [" + System.currentTimeMillis() + "]");
            // 当两个线程都达到该汇合点时,执行交换。
            this.data = this.exchanger.exchange(this.data);
            // 从这条记录的时间戳可以看出,达到汇合点的交换是同时执行的。
            System.out.println(Thread.currentThread() + " data after exchange " + this.data + "[" + System.currentTimeMillis() + "]");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

可以从时间戳看出,当两个线程都达到汇合点之后,才会执行交换。
在这里插入图片描述

5. ReentrantLock

重入锁,可以替代Synchronized关键字,对同步代码块加锁。
与Condition一起使用,实现线程之间的通信。

一个Condition对应一个ReentrantLock
一个ReentrantLock可以创建多个Condition, 不同condition之前的通信不互相影响。

DEMO:一个ReentrantLock对应一个Condition

package com.jimmy.concurrent.util;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockDemo {

    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    public void runAwaitTest() {
        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName() + " waiting.");
            Thread.sleep(2000);
            System.out.println(Thread.currentThread().getName() + " condition await.");
            // 释放锁并阻塞,直到重新获取到锁
            condition.await();
            System.out.println(Thread.currentThread().getName() + " continue...");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println(Thread.currentThread().getName() + " finish.");
            lock.unlock();
        }
    }

    public void runSignalTest() {
        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName() + " start.");
            Thread.sleep(2000);
            System.out.println(Thread.currentThread().getName() + " condition signal.");
            // 通知其他线程可以竞争锁
            condition.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println(Thread.currentThread().getName() + " finish.");
            lock.unlock();
        }
    }


    public static void main(String[] args) {
        ReentrantLockDemo demo = new ReentrantLockDemo();
        Thread t1 = new Thread(() -> {
            demo.runAwaitTest();
        }, "t1");

        Thread t2 = new Thread(() -> {
            demo.runSignalTest();
        }, "t2");

        t1.start();
        t2.start();
    }
}

执行结果:
在这里插入图片描述

DEMO:一个ReentrantLock对应多个Condition

package com.jimmy.concurrent.util;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockWithMultiConditionDemo {
    private Lock lock = new ReentrantLock();
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();


    public void runAwait1Test() {
        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName() + " waiting." + "[" + System.currentTimeMillis() + "]");
            System.out.println(Thread.currentThread().getName() + " condition-1 await." + "[" + System.currentTimeMillis() + "]");
            // 释放锁并阻塞,直到重新获取到锁
            condition1.await();
            System.out.println(Thread.currentThread().getName() + " continue..." + "[" + System.currentTimeMillis() + "]");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println(Thread.currentThread().getName() + " finish." + "[" + System.currentTimeMillis() + "]");
            lock.unlock();
        }
    }

    public void runAwait2Test() {
        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName() + " waiting." + "[" + System.currentTimeMillis() + "]");
            System.out.println(Thread.currentThread().getName() + " condition-1 await." + "[" + System.currentTimeMillis() + "]");
            // 释放锁并阻塞,直到重新获取到锁
            condition1.await();
            System.out.println(Thread.currentThread().getName() + " continue..." + "[" + System.currentTimeMillis() + "]");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println(Thread.currentThread().getName() + " finish." + "[" + System.currentTimeMillis() + "]");
            lock.unlock();
        }
    }

    public void runAwait3Test() {
        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName() + " waiting." + "[" + System.currentTimeMillis() + "]");
            System.out.println(Thread.currentThread().getName() + " condition-2 await." + "[" + System.currentTimeMillis() + "]");
            // 释放锁并阻塞,直到重新获取到锁
            condition2.await();
            System.out.println(Thread.currentThread().getName() + " continue..." + "[" + System.currentTimeMillis() + "]");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println(Thread.currentThread().getName() + " finish." + "[" + System.currentTimeMillis() + "]");
            lock.unlock();
        }
    }

    public void runSignalAllTest() {
        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName() + " start." + "[" + System.currentTimeMillis() + "]");
            System.out.println(Thread.currentThread().getName() + " condition-1 signalAll." + "[" + System.currentTimeMillis() + "]");
            // 通知其他线程可以竞争锁
            condition1.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println(Thread.currentThread().getName() + " finish." + "[" + System.currentTimeMillis() + "]");
            lock.unlock();
        }
    }

    public void runSignalTest() {
        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName() + " start." + "[" + System.currentTimeMillis() + "]");
            System.out.println(Thread.currentThread().getName() + " condition-2 signal." + "[" + System.currentTimeMillis() + "]");
            // 通知其他线程可以竞争锁
            condition2.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println(Thread.currentThread().getName() + " finish." + "[" + System.currentTimeMillis() + "]");
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        ReentrantLockWithMultiConditionDemo demo = new ReentrantLockWithMultiConditionDemo();
        Thread t1 = new Thread(() -> {
            demo.runAwait1Test();
        }, "t1");

        Thread t2 = new Thread(() -> {
            demo.runAwait2Test();
        }, "t2");

        Thread t3 = new Thread(() -> {
            demo.runAwait3Test();
        }, "t3");

        t1.start();  //c1
        t2.start();  //c1
        t3.start();  //c2

        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        Thread t4 = new Thread(() -> {
            demo.runSignalAllTest();
        }, "t4");
        t4.start(); // c1 all

        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        Thread t5 = new Thread(() -> {
            demo.runSignalTest();
        }, "t5");
        t5.start(); // c2
    }
}

执行结果:
可以看出,程序执行过程中,不同condition是不会相互影响的。condition-1 signalAll()只对t1、t2线程生效,使其获得锁可以继续执行。t3由于没有获取condition的signal()所以在继续阻塞。
在这里插入图片描述

6. ReentrantReadWriteLock

读写锁, 读写分离机制,读读共享,读写互斥、写写互斥。
在读多写少的场景下,效率高于ReentrantLock

公平性问题
所为公平,就是多个线程竞争锁的时候,以先到先得的方式来获得锁。
但是公平性的保证需要额外维护一套竞争锁的线程的顺序,因此效率会比非公平的情况下更低。
Semaphore和ReentrantLock都存在保证公平性的机制

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值