多线程工具类:CountDownLatch、CyclicBarrier、Semaphore、LockSupport

CountDownLatch

假如有一个任务想要往下执行,但必须要等到其他的任务执行完毕后才可以。

比如你想要买套房子,但是呢你现在手上没有钱。你得等这个月工资发了、然后年终奖发了、然后朋友借你得钱还给你了、然后再给朋友借一部分才可以买,这种场景你就可以使用CountDownLatch。

CountDownLatch是JDK为我们提供的一个计数器,它的操作是原子操作,同一时间只能有一个线程去操作这个它。

我们先来看一下CountDownLatch的主要方法。

//构造方法,接收计数器的数量
public CountDownLatch(int count)
//持续等待计数器归零
public void await()
//最多等待unit时间单位内timeout时间
public boolean await(long timeout, TimeUnit unit)
//计数器减1
public void countDown()
//返回现在的计数器数量
public long getCount()

下面是CountDownLatch的基本使用示例代码:

public class CountDownLatchDemo {
    public static CountDownLatch countDownLatch = new CountDownLatch(5);
    static class ThreadDemo extends Thread {
        @Override
        public void run() {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getId() + "完成任务");
            countDownLatch.countDown();
        }
    }
    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 5; i++) {
            new ThreadDemo().start();
        }
        countDownLatch.await();
        System.out.println("全部完成任务");
    }
}

CyclicBarrier

相比较于CountDownLatch,CyclicBarrier可以完成前者的全部功能,但是相比前者,它的功能更加的强大。

  1. CyclicBarrier翻译过来的中文名称叫循环栅栏,顾名思义它可以循环使用

  2. CyclicBarrier还可以接收一个Runnable对象,当栅栏循环一次技术后会执行一次Runnable

我们来看一下CyclicBarrier的常用方法:

//构造方法,第一个参数为栅栏饿长度,第二个就是上方所说的Runnable对象
public CyclicBarrier(int parties, Runnable barrierAction)
public CyclicBarrier(int parties)
//获取现在的数量
public int getParties()
//持续等待栅栏归零
public int await()
//最多等待unit时间单位内timeout时间
public int await(long timeout, TimeUnit unit)

下面是CyclicBarrier的基本使用示例代码:

public class CyclicBarrierDemo {
    public static CyclicBarrier cyclicBarrier = new CyclicBarrier(5,new FinallyThreadDemo());
    static class ThreadDemo extends Thread {
        @Override
        public void run() {
            try {
                Thread.sleep(1000);
                System.out.println(Thread.currentThread().getId() + "完成任务");
                cyclicBarrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
            System.out.println("到达屏障点每个线程都会瞬时继续执行");
        }
    }
    static class FinallyThreadDemo extends Thread {
        @Override
        public void run() {
            System.out.println("所有任务已经完成之后单独执行的任务!");
        }
    }
    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            new ThreadDemo().start();
        }
    }
}

观察打印结果我们可以发现:

当循环栅栏的任务执行完一轮以后,如果构造时传入了Runnable对象,则先执行Runnable对象,然后在瞬间释放所有任务的锁。

14完成任务
15完成任务
16完成任务
17完成任务
18完成任务
所有任务已经完成之后单独执行的任务!
到达屏障点每个线程都会瞬时继续执行
到达屏障点每个线程都会瞬时继续执行
到达屏障点每个线程都会瞬时继续执行
到达屏障点每个线程都会瞬时继续执行
到达屏障点每个线程都会瞬时继续执行
19完成任务
20完成任务
21完成任务
22完成任务
23完成任务
所有任务已经完成之后单独执行的任务!
到达屏障点每个线程都会瞬时继续执行
到达屏障点每个线程都会瞬时继续执行
到达屏障点每个线程都会瞬时继续执行
到达屏障点每个线程都会瞬时继续执行
到达屏障点每个线程都会瞬时继续执行

Semaphore

浅谈Java中的锁:Synchronized、重入锁、读写锁 一文中,我们了解了synch和读写锁,我们发现使用锁的时候一次只允许一条线程方法。那么有什么东西可以提供更强大的控制方法么?这个东西就是信号量。

信号量提供的主要方法:

//创建具有给定许可数的信号量
Semaphore(int permits):构造方法,创建
//拿走1个许可
void acquire()
//拿走多个许可
void acquire(int n)
//释放一个许可
void release()
//释放n个许可
void release(int n):
//当前可用的许可数
int availablePermits():

下面来看使用示例:

public class SemaphoreThreadDemo {
    public static Semaphore semaphore = new Semaphore(5);

    static class ThreadDemo extends Thread {
        @Override
        public void run() {
            try {
                semaphore.acquire();
                System.out.println(Thread.currentThread().getId() + "号线程在"+System.currentTimeMillis()+"获取资源");
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                semaphore.release();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 30; i++) {
            new ThreadDemo().start();
        }
    }
}

LockSupport

我们在 详解Thread多线程生产者消费者模型 两篇文章中使用过wait和notify实现了线程之间的协作,其实关于线程协作JDK还为我们提供了另外一个工具类LockSupport。

使用LockSupport实现等待通知功能时还不需要获取锁哦

先来看一下LockSupport的常用方法:

// 禁用当前线程
static void park()
// 如果参数线程的不可用,则使其可用。
static void unpark(Thread thread)

来看一下示例代码:

public class LockSupportThreadDemo {
    public static Thread thread;
    static class WaitThreadDemo extends Thread {
        @Override
        public void run() {
            System.out.println("WaitThread wait,time=" + System.currentTimeMillis());
            thread = Thread.currentThread();
            LockSupport.park();
            System.out.println("WaitThread end,time=" + System.currentTimeMillis());
        }
    }
    static class NotifyThreadDemo extends Thread {
        @Override
        public void run() {
            System.out.println("NotifyThread notify,time=" + System.currentTimeMillis());
            LockSupport.unpark(thread);
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("NotifyThread end,time=" + System.currentTimeMillis());
        }
    }
    public static void main(String[] args) {
        WaitThreadDemo waitThreadDemo = new WaitThreadDemo();
        NotifyThreadDemo notifyThreadDemo = new NotifyThreadDemo();
        waitThreadDemo.start();
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        notifyThreadDemo.start();
    }
}

文末彩蛋

针对于上面所涉及到的知识点我总结出了有1到5年开发经验的程序员在面试中涉及到的绝大部分架构面试题及答案做成了文档和架构视频资料免费分享给大家(包括Dubbo、Redis、Netty、zookeeper、Spring cloud、分布式、高并发等架构技术资料),希望能帮助到您面试前的复习且找到一个好的工作,也节省大家在网上搜索资料的时间来学习,也可以关注我一下以后会有更多干货分享。

资料获取方式: QQ群搜索“708-701-457” 即可免费领取

在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_42982923/article/details/89321812