控制并发流程

内容纲要

什么是控制并发流程?

  • 控制并发流程的工具类,作用就是帮助我们程序员更容易得让线程之间合作
  • 让线程之间相互配合,来满足业务逻辑
  • 比如让线程A等待线程B执行完毕后再执行等合作策略

有哪些控制并发流程的工具类?

CountDownLatch

  • 迸发流程控制的工具
    • 倒数门闩
    • 流程: 倒数结束之前,一直处于等待状态,直到倒计时结束,此线程才继续工作

      类的主要方法介绍

  • CountDownLatch(int count): 仅有一个构造函数,参数count为需要倒数的数值。
  • await(): 调用await0方法的线程会被挂起,它会等待直到count值为0才继续执行。
  • countDown():将count值减1,直到为0时,等待的线程会被唤起。

注意点

  • 扩展用法:多个线程等多个线程完成执行后,再同时执行
  • CountDownLatch是能够重用的,如果需要重新计数,可以考虑使用CyclicBarrier或者创建新的CountDownLatch实例。

Semaphore信号量

  • Semaphore可以用来限制或管理数量有限的资源的使用情况
  • 信号量的作用是维护一个“许可证”的计数,线程可以“获取许可证,那信号量剩余的许可证就减一,线程也可以“是否”个许可证,那信号量剩余的许可证就加一,当信号量所拥有的许可证数量为0,那么下一个还想要获取许可证的线程,就需要等待,直到有另外的线程释放了许可证












信号量使用流程

  • 初始化Semaphore并指定许可证的数量
  • 在需要被现在的代码前加acquire() 或者acquireUninterruptibly()方法
  • 在任务执行结束后,调用release() 来释放许可证
  • new Semaphore(int permits, boolean fain) : 这里可以设置是否要使用公平策略,如果传入true,那么Semaphore会把之前等待的线程放到FIFO的队列里,以便于当有了新的许可证可以分发给之前等了最长时间的线程
  • tryAcquire():看看现在有没有空闲的许可证,如果有的话就获取,如果没有的话也没关系,我不必陷入阻塞,我可以去做别的事,过一会再来查看许可证的空闲情况
  • tryAcquire(timeout): 和tryAcquire()一样,但是多了一个超时时间,比如“在3秒内获取不到许可证,我就去做别的事
public class semaphoreDemo {

    static Semaphore semaphore = new Semaphore(3,true);

    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(50);
        for (int i = 0; i < 100; i++) {
            service.submit(new Task());
        }
        service.shutdown();
    }

    static class Task implements Runnable{
        @Override
        public void run() {
            try {
                semaphore.acquire();
                System.out.println(Thread.currentThread().getName()+"拿到了许可证");
                Thread.sleep(1000);

            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }finally {
                System.out.println(Thread.currentThread().getName()+"释放了许可证");
                semaphore.release();
            }
        }
    }
}

注意点

  1. 获取和释放的许可证数量必须一致,否则比如每次都获取2个但是只释放1个甚至不释放,随着时间的推移,到最后许可证数量不够用,会导致程序卡死。(虽然信号量类并不对是否和获取的数量做规定,但是这是我们的编程规范,否则容易出错),业务层面的考虑
  2. 注意在初始化Semaphore的时候设置公平性,一般设置为true会更合理
  3. 并不是必须由获取许可证的线程释放那个许可证,事实上,获取和释放许可证对线程并无要求,也许是A获取了,然后由B释放,只要逻辑合理即可.
  4. 信号量的作用,除了控制临界区最多同时有N个线程访问4外,另一个作用是可以实现“条件等待”,例如线程1需要在线程2完成准备工作后才能开始工作,那么就线程1acquire0,而线程2完成任务后release0,这样的话,相当于是轻量级的CountDownLatch.

Condition接口(又称条件对象)

  • 当线程1需要等待某个条件的时候,它就去执行condition.await() 方法,一旦执行了await()方法,线程就会进入阻塞状态
  • 然后通常会有另外一个线程,假设是线程2,去执行对应的条件,直到这个条件达成的时候,线程2就会去执行conditicn.siqnal() 方法,这时JM就会从被阳塞的线程里找找到那些等待该condition的线程,当线程1就会收到可执行信号的时候,它的线程状态就会变成Runnable可执行状态

signalAll()和signal()区别

  • signalAIl()会起所有的正在等待的线程
  • 但是signal()是公平的,只会唤起那个等待时间最长的线程

Condition注意点

  • 实际上,如果说Lock用来代替synchronized,那么Condition就是用来代替相对应的Object.wait/notify的,所以在用法和性质上,几乎都一样
  • await方法会自动释放持有的Lock锁,和Obiect.wait一样,不需要自己手动先释放锁
  • 调用await的时候,必须持有锁,否则会抛出异常,和Object.wait一样

CyclicBarrier循环栅栏

  • CyclicBarrier循环栅栏和CountDownLatch很类似,都能阻塞组线程
  • 当有大量线程相互配合,分别计算不同任务,并且需要最后统汇总的时候,我们可以使用CyclicBarrier。CyclicBarrier可以构造一个集结点,当某一个线程执行完毕,它就会到集结点等待,直到所有线程都到了集结点,那么该栅栏就被撤销,所有线程再统一出发,继续执行剩下的任务

CyclicBarrier和CountDownLatch的区别

  • 作用不同: CyclicBarrier要等固定数量的线程都到达了栅栏位置才能继续执行,而CountDownLatch只需等待数字到0,也就是说,CountDownLatch用于事件,但是CyclicBarrier是用于线程的。
  • 可重用性不同:CountDownLatch在倒数到0并触发门门打打开后,就不能再次使用了,除非新建新的实例;而CyclicBarrier可以重复使用。
THE END
分享
二维码
< <上一篇
下一篇>>