JUC(java.util.concurrent) 的常⻅类

目录

Callable接口

ReentrantLock

[synchronized 和 ReentrantLock之间的区别](#synchronized 和 ReentrantLock之间的区别)

信号量Semaphore

CountDownLatch


JUC(java.util.concurrent) 的常⻅类

Callable接口

Callable 是⼀个 interface . 相当于把线程封装了⼀个 "返回值". ⽅便程序猿借助多线程的⽅式计算结果

Callable接口和Runnable接口并列关系

返回值call()

泛型参数
代码⽰例: 创建线程计算 1 + 2 + 3 + ... + 1000, 使⽤ Callable 版本

java 复制代码
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

public class Demo40 {
    public static void main(String[] args) {
        //此处Callable只是定义了一个"带有返回值"的任务
        //并没有真的在执行,执行还是需要搭配Thread对象
        Callable<Integer> callable = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                int ret = 0;
                for (int i = 1; i <= 100; i++) {
                    ret += i;
                }
                return ret;
            }
        };
        FutureTask<Integer> futureTask = new FutureTask<>(callable);
        Thread t = new Thread(futureTask);
        t.start();
        System.out.println(futureTask.get());
    }
}

Thread的构造方法,没有提供版本传入Callable对象

Thread本身不提供获取结果的方法

就需要凭FutureTask对象来拿到结果
**代码⽰例: ** 创建线程计算 1 + 2 + 3 + ... + 1000, 不使⽤ Callable 版本

java 复制代码
public class Demo41 {
    private static  int total = 0;
    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                int sum = 0;
                for (int i = 1; i <= 100; i++) {
                    sum += 0;
                }
                total = sum;
            }
        };
        Thread t =new Thread(runnable);
        t.start();
        t.join();
        System.out.println(total);
    }
}

理解 Callable
Callable 和 Runnable 相对, 都是描述⼀个 "任务". Callable 描述的是带有返回值的任务, Runnable
描述的是不带返回值的任务.
Callable 通常需要搭配 FutureTask 来使⽤. FutureTask ⽤来保存 Callable 的返回结果. 因为
Callable 往往是在另⼀个线程中执⾏的, 啥时候执⾏完并不确定.
FutureTask 就可以负责这个等待结果出来的⼯作.
理解 FutureTask
想象去吃⿇辣烫. 当餐点好后, 后厨就开始做了. 同时前台会给你⼀张 "⼩票" . 这个⼩票就是
FutureTask. 后⾯我们可以随时凭这张⼩票去查看⾃⼰的这份⿇辣烫做出来了没


ReentrantLock

可重⼊互斥锁. 和 synchronized 定位类似, 都是⽤来实现互斥效果, 保证线程安全.

和synchronized是并列关系

更经典风格的锁

java 复制代码
import java.util.concurrent.locks.ReentrantLock;

public class Demo42 {
    private static int count = 0;
    public static void main(String[] args) {
        ReentrantLock locker = new ReentrantLock();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 5000; i++) {
                    locker.lock();
                    try{
                        count++;
                    }finally {
                        locker.unlock(); 
                    }
                }
            }
        });
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 5000; i++) {
                    locker.lock();
                    try{
                        count++;
                    }finally {
                        locker.unlock();
                    }
                }
            }
        });
        t1.start();
        t2.start();
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        
    }
}

synchronized 和 ReentrantLock之间的区别

1.synchronized 是关键字(内部实现是jvm内部通过c++实现的)ReentrantLock标准库的类(Java)

2.synchronized通过代码块控制加锁解锁,ReentrantLock需要lock/unlock方法,需要注意unlock不被调用的问题

3.ReentrantLock除了提供lock,unlock之外还提供了一个方法tryLock()

tryLock不会阻塞

加锁成功返回true,失败返回false

调用者判定返回值决定接下来咋做

设置超时时间

等待时间达到超时时间在返回true/false

4.ReentrantLock提供了公平锁的实现

java 复制代码
ReentrantLock locker = new ReentrantLock(true);

默认是非公平的

5.ReentrantLock搭配的等待通知机制是Condition类,相比wait,notify来说功能更强大一些


信号量Semaphore

信号量, ⽤来表⽰ "可⽤资源的个数". 本质上就是⼀个计数器

能够协调多个进程之间的资源分配

也能协调多个线程之间的资源分配

申请acquire一个资源,计数器就会-1,p操作

释放release一个资源,计数器就会+1,v操作

计数器为0,继续申请就会阻塞等待

java 复制代码
import java.util.concurrent.Semaphore;

public class Demo43 {
    public static void main(String[] args) throws InterruptedException {
        Semaphore semaphore = new Semaphore(4);
        semaphore.acquire();
        System.out.println("进行一次p操作");
        semaphore.acquire();
        System.out.println("进行一次p操作");
        semaphore.acquire();
        System.out.println("进行一次p操作");
        semaphore.acquire();
        System.out.println("进行一次p操作");
    }
}

信号量的一个特殊情况,初始值为1 的信号量

取值要么是1要么是0(二元信号量)

等价于"锁"(普通的信号量,就相当于锁的更广泛的推广)

如果是普通的N的信号量,就可以限制同时有多少个线程来执行某个逻辑

java 复制代码
import java.util.concurrent.Semaphore;

public class Demo44 {
    private static int count = 0;
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(1);
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 5000; i++) {
                    try {
                        semaphore.acquire();
                        count++;
                        semaphore.release();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    
                }
            }
        });
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 5000; i++) {
                    try {
                        semaphore.acquire();
                        count++;
                        semaphore.release();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }

                }
            }
        });
        t1.start();
        t2.start();
    }
}

CountDownLatch

同时等待N个任务执行结束

使用多线程经常把一个大的任务拆分成多个子任务

使用多线程执行这些子任务,从而提高程序的效率

1.构造方法指定参数,描述拆成了多少个任务

2.每个任务执行完毕之后,都调用一次countDown方法

如果指定10个任务,当一共调用了10次countDown说明任务全都完成了

3.主线程中调用await方法,等待所以任务执行完毕

await就会返回/阻塞等待

java 复制代码
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Demo45 {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(10);
        ExecutorService executor = Executors.newFixedThreadPool(4);
        for (int i = 0; i < 10; i++) {
            int id = i;
            executor.submit(()->{
                System.out.println("子任务开始执行"+id);
                try {
                    Thread.sleep(1000);
                }catch (InterruptedException e){
                    throw new RuntimeException(e);
                }
                System.out.println("子任务结束执行"+id);
            });

        }
        latch.await();//阻塞等待所有的任务结束
        System.out.println("所有任务执行完毕");
        executor.shutdown();
    }
}
相关推荐
界面开发小八哥4 分钟前
更高效的Java 23开发,IntelliJ IDEA助力全面升级
java·开发语言·ide·intellij-idea·开发工具
草莓base17 分钟前
【手写一个spring】spring源码的简单实现--容器启动
java·后端·spring
Allen Bright31 分钟前
maven概述
java·maven
qystca33 分钟前
洛谷 B3637 最长上升子序列 C语言 记忆化搜索->‘正序‘dp
c语言·开发语言·算法
编程重生之路33 分钟前
Springboot启动异常 错误: 找不到或无法加载主类 xxx.Application异常
java·spring boot·后端
薯条不要番茄酱33 分钟前
数据结构-8.Java. 七大排序算法(中篇)
java·开发语言·数据结构·后端·算法·排序算法·intellij-idea
今天吃饺子38 分钟前
2024年SCI一区最新改进优化算法——四参数自适应生长优化器,MATLAB代码免费获取...
开发语言·算法·matlab
努力进修42 分钟前
“探索Java List的无限可能:从基础到高级应用“
java·开发语言·list
politeboy43 分钟前
k8s启动springboot容器的时候,显示找不到application.yml文件
java·spring boot·kubernetes
Daniel 大东2 小时前
BugJson因为json格式问题OOM怎么办
java·安全