前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【多线程】JUC的常见类,Callable接口,ReentranLock,Semaphore,CountDownLatch

【多线程】JUC的常见类,Callable接口,ReentranLock,Semaphore,CountDownLatch

原创
作者头像
椰椰椰耶
发布2024-09-20 10:33:16
660
发布2024-09-20 10:33:16
  • 基于 Condition 类,功能要比 wait/notify 更强一些

三、信号量 Semaphore

也称为“信号灯”(开船的水手,旗语)

你去车库停车,如何知道是否还有空位?

现在的停车场,一般的入口处,就会有一个“电子牌”,会显示有多少个空闲车位

  • 有车开进去了,上述的计数 -1
  • 有车开出来了,上述的计数 +1
  • 如果计数为 0 了,说明没空位了

这里的“电子牌”就像是一个“信号量”,信号量是一个计数器,通过计数器衡量“可用资源”的个数

  • 申请资源(acquire):让计数器 -1(“P”操作
  • 释放资源(release):让计数器 +1(“V”操作
  • 如果计数器为 0 了,继续申请,就会出现阻塞 所以信号量的操作也称为“PV 操作

操作系统本身提供了信号量实现,JVM 把操作系统的信号量封装了一下,我们直接使用就可以了

代码语言:java
复制
import java.util.concurrent.Semaphore;  
  
public class Demo5 {  
    public static void main(String[] args) throws InterruptedException {  
        //参数为可用资源的个数,计数器的初始值  
        Semaphore semaphore = new Semaphore(3);  
  
        semaphore.acquire();  
        System.out.println("申请一个资源1");  
        semaphore.acquire();  
        System.out.println("申请一个资源2");  
        semaphore.acquire();  
        System.out.println("申请一个资源3");  
        semaphore.acquire();  
        System.out.println("申请一个资源4");  
    }
}
//运行结果
申请一个资源1
申请一个资源2
申请一个资源3
  • 初始化的信号量为 3
  • 申请了三个资源后,没空位了,计数器为 0 了,就堵塞住了

若在这里面释放一次资源,就可以将资源 4 申请进去:

代码语言:java
复制
import java.util.concurrent.Semaphore;  
  
public class Demo5 {  
    public static void main(String[] args) throws InterruptedException {  
        //参数为可用资源的个数,计数器的初始值  
        Semaphore semaphore = new Semaphore(3);  
  
        semaphore.acquire();  
        System.out.println("申请一个资源1");  
        semaphore.acquire();  
        System.out.println("申请一个资源2");  
        semaphore.acquire();  
        System.out.println("申请一个资源3");  
        semaphore.release();  
        System.out.println("释放一个资源");  
        semaphore.acquire();  
        System.out.println("申请一个资源4");  
    }
}
//运行结果
申请一个资源1
申请一个资源2
申请一个资源3
释放一个资源
申请一个资源4

平替加锁解锁

  • 在需要加锁的时候,可以设置一个信号量,初始化一个资源import java.util.concurrent.Semaphore; public class Demo4 { private static int count = 0; public static void main(String[] args) throws InterruptedException { Semaphore semaphore = new Semaphore(1); Thread t1 = new Thread(() -> { for (int i = 0; i < 50000; i++) { try { semaphore.acquire(); } catch (InterruptedException e) { throw new RuntimeException(e); } count++; semaphore.release(); } }); Thread t2 = new Thread(() -> { for (int i = 0; i < 50000; i++) { try { semaphore.acquire(); } catch (InterruptedException e) { throw new RuntimeException(e); } count++; semaphore.release(); } }); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println("count=" + count); } }
  • 谁要用的时候就申请这个资源,用完之后再释放
  • 这样的话,申请到唯一资源的线程执行操作的时候,就不会有其他的线程进行操作了
  • 这种值为 1 的信号量,就相当一个锁,资源要么是 1,要么是 0,所以也称为“二元信号量

四、CountDownLatch

“锁存器”

很多时候,需要把一个大的任务,拆成多个小的任务,通过多线程/线程池来执行。如何衡量,所有的任务都执行完毕了?

  • 比如“多线程下载”
  • 浏览器的下载,一般是单线程的,下载速度是有限的(一秒 2-3 MB)
  • 但是可以通过多线程的方式提高下载速度 就可以使用专门的下载工具,通过多个线程,和服务器建立多个网络连接(服务器进行网速限制都是针对一个连接做出的限制),那如果我创建 10-20 个线程,那么下载的总速度就能大幅度提高
  • 多个线程进行一起操作,每个线程下载一部分,所有线程下载完毕再进行拼装
代码语言:java
复制
import java.util.concurrent.CountDownLatch;  
import java.util.concurrent.ExecutorService;  
import java.util.concurrent.Executors;  
  
public class Demo6 {  
    public static void main(String[] args) throws InterruptedException {  
        ExecutorService executorService = Executors.newFixedThreadPool(4);  
  
        //构造方法的个数,就是拆分出来的任务数量  
        CountDownLatch countDownLatch = new CountDownLatch(20);  
  
        for (int i = 0; i < 20; i++) {  
            int id = i;  
            executorService.submit(() -> {  
                System.out.println("下载任务" + id + "开始执行");  
                try {  
                    Thread.sleep(3000);  
                } catch (InterruptedException e) {  
                    throw new RuntimeException(e);  
                }                
                System.out.println("下载任务" + id + "结束执行");  
                //完毕 over!  
                countDownLatch.countDown();  
            });        
        }        
        // 当 countDownLatch 收到了 20 个“完成”,所有的任务就都完成了  
        // await => all wait  
        // await 这个词是计算机术语,“等待所有”  
        countDownLatch.await();  
  
        System.out.println("所有任务都完成");  
    }
}
  • CountDownLatch 一般都是结合线程池进行使用
  • 借助 CountDownLatch 就能衡量出当前任务是否整体执行结束

上面这些再实际开发中用的布套多,但面试可能问到,特别是“ReentrantLock”和“Semaphore”

五、相关面试题

image.png
image.png

image.png
image.png

image.png
image.png

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、Callable 接⼝
  • 二、ReentrantLock
    • synchronized 和 ReentrantLock 的差异
    • 三、信号量 Semaphore
      • 平替加锁解锁
      • 四、CountDownLatch
      • 五、相关面试题
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档