JUC:并发工具类

CountDownLatch ,CyclicBarrier 和 Semaphore 工具类提供一种并发流程控制的手段
Exchanger 工具类提供了在线程间交换数据的一种手段

CountDownLatch

主要作用是:允许一个或多个线程等待其他线程完成,然后再继续往下执行
它的作用和 join 类似,但是功能比 join 更加强大,使用起来也更灵活

join 的实现原理是,让当前线程等待 join 线程执行结束,在这个过程中会不停的检查 join 线程是否存活,如果还存活着,那么当前线程就会处于无限等待,也就是调用了 wait() 方法让当前线程等待,当 join 线程执行结束或者异常终止后,JVM 会调用 notifyAll() 方法唤醒当前线程,然后当前线程往下执行

CountDownLatch 继承了 AQS 中的共享锁模板方法,通过一个 state 也就是 int 的数据类型的值来表示一个锁计数器,通过在构造 CountDownLatch 输入这个计数器的数量,CountDownLatch 通过 await() 和 countDown 方法来实现线程之间的通信,每当调用 countDown 方法就会将计数器 -1 ,当计数器为 0 时,被阻塞的线程才可以继续往下执行

public class CountDownLatchTest {

    static CountDownLatch c = new CountDownLatch(2);

    public static void main(String[] args) throws InterruptedException {
        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 不能重新初始化和修改已经创建对象中的计数器的值,这点和 CyclicBarrier 进行区分

CyclicBarrier (同步屏障)

作用:让一组线程到达一个屏障或者同步点后就被阻塞,直到最后一个线程到达这个屏障后,屏障才会被开启,所有被阻拦的线程才会接着往下执行

默认在构造方法的参数中设置阻拦的线程数量,到达同步点的线程通过调用 await() 方法告诉 CyclicBarrier 我已经到达屏障了,然后就会阻塞住

public class CyclicBarrierTest {
    static CyclicBarrier c = new CyclicBarrier(3);

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

        try {
            c.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        }
        System.out.println(2);
    }
}

还有一个高级的构造方法,不仅设置了阻拦的线程数量,还设置了优先执行的任务,意思是当最后一个线程到达屏障后,先执行构造方法中指定的任务,之后才执行其他被阻塞的线程

public class CyclicBarrierTest2 {

    static CyclicBarrier c = new CyclicBarrier(2,new A());

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

        try {
            c.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        }
        System.out.println(2);
    }

    static class A implements Runnable{
        @Override
        public void run() {
            System.out.println(3);
        }
    }
}

应用场景:多线程计算数据,然后最终合并统计计算的场景

Semaphore (信号量)

作用:用来控制同时访问特定资源的线程数量,通过协调线程,来达到合理化的使用公共资源
举例:一个条马路上有一个红绿灯,这条马路只允许 100 辆车同时行驶,那么超过 100 辆车后面的车都会在路口进行等待,就好像后 100 辆车只能看到红灯,前 100 辆车看到绿灯可以开入这条公路,当前100 辆车中有 10 辆车离开这条马路了,那么后面就可以开入 10 辆车进入这条马路

用法:线程会尝试通过 acquire() 来获取通行证,如果当前线程数量还在 semaphore 的限度范围内,那该线程就可以获得通行证,如果超过 semaphore 的限度,那么就只能等待,当获取通行证的线程任务执行结束后,通过 release() 方法归还这个通行证

public class SemaphoreTest {

    private static final int THREAD_COUNT = 30;

    private static ExecutorService pool = 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++) {
            pool.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        s.acquire();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
        pool.shutdown();
    }
}

应用场景:用做流量控制,例如公共资源有限情况下的场景,像数据库连接

Exchanger

作用:用于线程之间的数据交换,它提供一个同步点,当两个线程到达这个同步点后就可以交换彼此的数据,如果第一个线程到达同步点后执行 exchange() 方法,那么他会一直等待另一个线程达到同步点也执行 exchange() 方法时,两个线程才能交换数据
但是如果某一个线程因为异常等特殊原因没有到达同步点执行 exchange() 方法,那么到达的那个线程就要一直等待下去,所以为了避免这种等待,可以设置一个最大的等待时间

public static final Exchanger<String> exgr = new Exchanger<>();

    public static ExecutorService pool = Executors.newFixedThreadPool(2);

    public static void main(String[] args) {
        pool.execute(new Runnable() {
            @Override
            public void run() {
                String A = "B";
                try {
                    exgr.exchange(A);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        pool.execute(new Runnable() {
            @Override
            public void run() {
                try {
                String B ="B";
                String A = exgr.exchange("B");
                    System.out.println("是否一致"+A.equals(B)+"A:::"+A+"B:::"+B);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        pool.shutdown();
    }
}

LockSupport 并发工具类的使用

public class LockSupportTest {

    public static void main(String[] args) throws InterruptedException {
        String a = new String("A");
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("睡觉");
                LockSupport.park(a);
                System.out.println("起床");
            }
        });
        t.setName("A-Name");
        t.start();
        Thread.sleep(300000);
        System.out.println("妈妈喊我起床");
        LockSupport.unpark(t);
    }
}

里面都是静态方法,功能是让线程在任意位置阻塞,也可以在任意位置唤醒线程
两个主要方法:park(阻塞线程)和 unpark(唤醒线程)
其底层实现是通过 unsafe 类来实现

你可能感兴趣的:(Java)