JavaEE中的JUC的常见的类

文章目录

  • JUC
    • 1. Callable接口
    • 2.ReentrantLock
    • 3.原子类
    • 4.线程池
    • 5. 信号量Semaphore
    • 6.CountDownLatch
  • 总结


JUC

JUC => java,util.concurrent 并发

这个包里放的东西都是跟多线程相关的~

1. Callable接口

类似于Runnable.

Runnable描述的任务,不带返回值
Callable描述的任务是带返回值的!!

如果多线程完成的任务,希望带上结果,就使用Callable比较好~

代码实现:
通过Callable创建线程,来计算 1+2+3+…+1000:

public class TeseDemo {
    //创建线程,通过线程来计算 1+2+3+...+1000

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //使用Callable来定义一个任务
        Callable<Integer> callable = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                int sum = 0;
                for (int i = 1; i <= 1000; i++) {
                    sum+=i;
                }
                return sum;
            }
        };

        FutureTask futureTask = new FutureTask<>(callable);

        //创建线程,来执行上述任务
        //Thread的构造方法,不能直接传callable,还需要一个中间的类
        Thread thread = new Thread(futureTask);

        thread.start();
        //获取线程的计算结果
        //get方法会阻塞,直到 call 方法计算完毕, get 才会返回~
        System.out.println(futureTask.get());
    }
}

在这里插入图片描述

FutureTask存在的意义就是为了让我们能够获取到结果(获取到结果的凭证),比如去食堂吃饭的时候,给你一个号,你的做好了,就喊你的号

线程创建的方式:

  1. 继承Thread(可以使用匿名内部类,也可以不用)
  2. 使用 Runnable(可以使用匿名内部类,也可以不用)
  3. 使用 lambda
  4. 使用线程池
  5. 使用 Callable

2.ReentrantLock

ReentrantLock是可重入互斥锁~

ReentrantLock 也是可重入锁. “Reentrant” 这个单词的原意就是 “可重入”

synchronized 也是可重入锁,但是有一些操作是做不到的,ReentrantLock是对synchronized的一个补充

ReentrantLock核心用法,三个方法:

  1. lock()加锁
  2. unlock()解锁
ReentrantLock locked = new ReentrantLock();
        //加锁
        locked.lock();
        //解锁
        locked.unlock();

这两节之间如果有return或者有异常
就可能导致unlock执行不到了!

synchronized则没有这个问题,只要代码出了代码块,就一定执行解锁~~

JavaEE中的JUC的常见的类_第1张图片
改成这样就可以解决这个问题了~

但是ReentrantLock的优点比缺点更耀眼,它有一些特定的功能,是synchronized做不到的:

  1. tryLock试试看能不能加上锁,试成功,就加锁成功,试失败了,就放弃~ 并且还可以指定加锁的等待超时时间~实际开发中,使用"死等的策略"要慎重,tryLock就提供了更多的可能
  2. ReentrantLock可以实现 公平锁~ 默认是非公平的,构造的时候,传入一个简单参数,就成了公平锁~
    在这里插入图片描述
  3. synchronized是搭配wait/notify 实现等待通知机制的,唤醒操作是随机唤醒一个等待的线程,ReentrantLock是搭配Condition类实现的,唤醒操作可以指定哪个等待的线程的

synchronizedReentrantLock的区别: 缺点+优势

在这里插入图片描述

3.原子类

原子类的底层,是基于CAS实现的,以比较高效的方式完成线程安全的自增自减~

Java里已经封装好了,可以直接来使用
JavaEE中的JUC的常见的类_第2张图片

使用原子类,最常见的场景就是使用多线程计数

比如写了个服务器,要计算一共有多少个并发量,就可以通过这样的原子变量来累加~

原子类的使用代码:

import java.util.concurrent.atomic.AtomicInteger;

/**
 * @Author YuanYuan
 * @Date 2022/9/25
 * @Time 21:17
 */
public class TestDemo2 {
    public static void main(String[] args) throws InterruptedException {
        AtomicInteger count = new AtomicInteger(0);
        Thread t1 = new Thread(()-> {
            for (int i = 0; i < 50000; i++) {
                //相当于count++
                count.getAndIncrement();
//                //相当于++count
//                count.incrementAndGet();
//                //相当于count--
//                count.getAndIncrement();
//                //相当于--count
//                count.decrementAndGet();
            }
        });
        Thread t2 = new Thread(()-> {
            for (int i = 0; i < 50000; i++) {
                //相当于count++
                count.getAndIncrement();
//                //相当于++count
//                count.incrementAndGet();
//                //相当于count--
//                count.getAndIncrement();
//                //相当于--count
//                count.decrementAndGet();
            }
        });
        t1.start();
        t2.start();

        t1.join();
        t2.join();
        //get()获取到内部的值
        System.out.println(count.get());
    }
}


运行结果:
在这里插入图片描述
可以看见没有任何加锁,借助CAS,完成了一个原子的累加。

在这里插入图片描述

4.线程池

JavaEE多线程中的 单例模式与线程池

5. 信号量Semaphore

信号量这个概念是大佬 迪杰斯特拉 提出来的。

信号量的基本操作是两个:

  1. P操作,申请一个资源
  2. V操作,释放一个资源

信号量本身是一个计数器,表示可用资源的个数:

P操作申请一个资源,可用资源数就 - 1
V操作释放一个资源,可用资源数就 + 1

当计数为0的时候,继续P操作,就会产生阻塞,阻塞等待到其他线程V操作了为止~~

基于信号量也是可以实现生产者消费者模型

信号量可以视为是一个更广义的锁!!

锁就是一个特殊的信号量(可用资源只有一个 1 的信号量)

把互斥锁也看作是计数为1的信号量(取值只有1和0,也叫做二元信号量)

JAVA标准库提供了Semaphore这个类,其实就是把 操作系统 提供的信号量封装了一下

信号量互斥例子:

JavaEE中的JUC的常见的类_第3张图片
可以看到,只打印了4个P操作,因为可用资源已经没有了,所以在第五个acquire()处就会发生阻塞,这个阻塞就会一直阻塞,直到其他线程进行释放

当需求中,有多个可用资源的时候,就记得要使用信号量~

P 和 V 是荷兰语,P 和 V 是荷兰语中申请和释放的首字母~
在这里插入图片描述

6.CountDownLatch

类似于一个跑步比赛 ~
当最后一个选手到达终点,比赛就结束~

使用CountDownLatch 就是类似的效果
使用的时候先设置以下有几个选手~
每个选手撞线了,就调用一下countDown方法,当撞线的次数达到了选手的个数,就认为比赛结束了~

比如使用多线程完成一个任务~

例如,要下载一个很大的文件,就切分成多个部分,每个线程负责下载其中的一个部分~
当所有的线程都下载完毕,整个文件就下载完毕了~

import java.util.concurrent.CountDownLatch;


public class countdownlatch {

    public static void main(String[] args) throws InterruptedException {
        //有5份文件需要下载
        CountDownLatch countDownLatch = new CountDownLatch(5);
        for (int i = 0; i < 5; i++) {
            //创建5个线程来执行一批任务
            Thread thread = new Thread(()-> {
                System.out.println("开始执行任务" + Thread.currentThread().getName());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("结束执行任务" + Thread.currentThread().getName());
                //记录完成
                countDownLatch.countDown();
            });
            thread.start();
        }

        //await进行阻塞等待,会等到所有的文件都下载完毕之后,才解除阻塞
        countDownLatch.await();
        System.out.println("文件下载完成!");
    }
}

再举个例子:

JavaEE中的JUC的常见的类_第4张图片

比如有一个服务挂了,那么响应回不来了怎么办?
await操作,是可以指定超时时间
可以加个参数:在这里插入图片描述

如果时间到了还没有收到相应,那就不等了~

总结

在这里插入图片描述

你可以叫我哒哒呀
本篇到此结束
“莫愁千里路,自有到来风。”
我们顶峰相见!

你可能感兴趣的:(java-ee,java,开发语言)