多线程——Callable,ReentrantLock,Semaphore,CountDownLatch

发布时间:2024年01月17日

Callable接口

1.继承了Thread(包含了匿名内部类的方法)
2.实现了Runnable(包含了匿名内部类的方式)
3.基于lambda
4.基于线程池

Runnable与Callable的区别:
Runnable关注的是这个过程,不关注执行结果
Runnable提供的run方法,返回值类型是void
Callable关注过程,也关注结果
Callab提供的call方法,返回值就是线程执行任务得到的结果

举例说明:
创建一个新线程,用新线程实现1+2+3+4+…+1000;
使用Thread写的代码:


public class Test16 {
    public static int sum = 0;
    public static void main(String[] args) throws InterruptedException {

        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                int result = 0;
                for (int i = 1; i <= 1000; i++) {
                    result+=i;
                }
                sum = result;
            }
        });
        t.start();
        t.join();
        System.out.println("sum=" + sum);
    }
}

这样写的代码不美观,且如果有多个线程就要创建多个成员变量来接收result。

使用Callable写的代码:

public class Test17 {
    public static void main(String[] args) throws ExecutionException,InterruptedException {
        Callable<Integer> callable = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                int result = 0;
                for (int i = 1; i <= 1000 ; i++) {
                    result+=i;
                }
                return result;
            }

        };
        FutureTask<Integer> task = new FutureTask<>(callable);
        Thread t = new Thread(task);
        t.start();
        System.out.println(task.get());
    }

代码解释:
创建?个匿名内部类, 实现 Callable 接?. Callable 带有泛型参数. 泛型参数表?返回值的类型.
? 重写 Callable 的 call ?法, 完成累加的过程. 直接通过返回值返回计算结果.
? 把 callable 实例使? FutureTask 包装?下.
? 创建线程, 线程的构造?法传? FutureTask . 此时新线程就会执? FutureTask 内部的 Callable 的call ?法, 完成计算. 计算结果就放到了FutureTask 对象中.
? 在主线程中调? futureTask.get() 能够阻塞等待新线程计算完毕. 并获取到 FutureTask 中的结果。
FutureTask类,作为Thread和callable的粘合剂

理解 Callable
Callable 和 Runnable 相对, 都是描述?个 “任务”. Callable 描述的是带有返回值的任务, Runnable描述的是不带返回值的任务.
Callable 通常需要搭配 FutureTask 来使?. FutureTask ?来保存 Callable 的返回结果. 因为Callable 往往是在另?个线程中执?的, 啥时候执?完并不确定.
FutureTask 就可以负责这个等待结果出来的?作.

理解 FutureTask
想象去吃?辣烫. 当餐点好后, 后厨就开始做了. 同时前台会给你?张 “?票” . 这个?票就是FutureTask. 后?我们可以随时凭这张?票去查看??的这份?辣烫做出来了没.

ReentrantLock

可重入互斥锁,和synchronized定位类似,都是来实现互斥效果,保证线程安全
ReentrantLock也是可重入锁,
ReentrantLock的用法:
lock( ) : 加锁,如果获取不到锁就死等
trylock(超时间):加锁,如果获取不到锁,等待一段时间之后就放弃加锁
unlock():解锁

ReentrantLock lock = new ReentrantLock(); 
-----------------------------------------
lock.lock(); 
try { 
 // working 
} finally { 
 lock.unlock() 
} 

ReentrantLock和synchronized的区别
1.ReentrantLock提供了tryLock操作
2.ReentrantLock提供了公平锁的实现
synchronized是非公平锁
ReentrantLock构造方法中填写参数,就可以设置成公平锁
3.搭配的等待通知机制不同的
对于synchronized搭配wait/notify
对于ReentrantLock搭配Condition类,功能比wait notify略强一点

信号量Semaphore

信号量,用来表示“可用资源的个数”,本质上就是一个计时器
如何理解信号量

可以把信号量想象成是停?场的展?牌: 当前有?位 100 个. 表?有 100 个可?资源.
当有?开进去的时候, 就相当于申请?个可?资源, 可??位就 -1 (这个称为信号量的 P 操作)
当有?开出来的时候, 就相当于释放?个可?资源, 可??位就 +1 (这个称为信号量的 V 操作)
如果计数器的值已经为 0 了, 还尝试申请资源, 就会阻塞等待, 直到有其他线程释放资源.

Semaphore 的 PV 操作中的加减计数器操作都是原?的, 可以在多线程环境下直接使?.

代码示例

? 创建 Semaphore ?例, 初始化为 4, 表?有 4 个可?资源.
? acquire ?法表?申请资源(P操作), release ?法表?释放资源(V操作)
? 创建 20 个线程, 每个线程都尝试申请资源, sleep 1秒之后, 释放资源. 观察程序的执?效果.

public class Test18 {
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(4);
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                try{
                    System.out.println("申请资源");
                    semaphore.acquire();
                    System.out.println("获取到了资源");
                    Thread.sleep(1000);
                    System.out.println("我释放了资源");
                    semaphore.release();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

            }
        };
        for (int i = 0; i < 20; i++) {
            Thread t = new Thread(runnable);
            t.start();
        }
    }
}

CountDownLatch

同时等待 N 个任务执?结束.

好像跑步?赛,10个选?依次就位,哨声响才同时出发;所有选?都通过终点,才能公布成绩。

? 构造 CountDownLatch 实例, 初始化 10 表?有 10 个任务需要完成.
? 每个任务执?完毕, 都调? latch.countDown() . 在 CountDownLatch 内部的计数器同时?
减.
? 主线程中使? latch.await(); 阻塞等待所有任务执?完毕. 相当于计数器为 0 了.

import java.util.Random;
import java.util.concurrent.CountDownLatch;


public class Test19 {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(10);
        for (int i = 0; i < 10; i++) {
            int id = i;
            Thread t = new Thread(()->{
                Random random = new Random();
                int time = (random.nextInt(5)+1)*1000;
                System.out.println("线程"+id+"开始下载");
                try {
                    Thread.sleep(time);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("线程"+id+"下载完成");
                countDownLatch.countDown();
            });
            t.start();
        }
        countDownLatch.await();
        System.out.println("全部下载完成");
    }
}

文章来源:https://blog.csdn.net/weixin_65476629/article/details/135606791
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。