JUC并发编程详解

news2024/11/27 22:41:30

JUC并发编程详解

  • 一、Callable接口
  • 二、ReentrantLock类
  • 三、原子类
  • 四、线程池
  • 五、信号量Semaphore
  • 六、闭锁CountDownLatch
  • 七、线程安全的集合类
    • 7.1 多线程环境使用 ArrayList
    • 7.2 多线程环境使用队列
    • 7.3 多线程环境使用哈希表

JUC => java.util.concurrent,这个包里放的东西都是和多线程相关的!

一、Callable接口

Callable是一个interface,相当于把线程封装了一个 “返回值”,方便程序猿借助多线程的方式计算结果。

需要用到FutureTask类:
在这里插入图片描述

代码示例: 创建线程计算 1 + 2 + 3 + … + 1000,使用 Callable 版本

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class Demo {
    // 创建线程, 通过线程来计算 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<Integer> futureTask = new FutureTask<>(callable);

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

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

若使用Thread类,需要一个辅助类,还需要使用一系列的加锁和 wait notify 操作,代码复杂、容易出错!
因此如果当前多线程完成的任务希望带上结果,使用 Callable 就比较好!

目前为止:
在这里插入图片描述

二、ReentrantLock类

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

ReentrantLock 也是可重入锁,“Reentrant” 这个单词的意思就是 “可重入”。

ReentrantLock 的用法:

  • lock():加锁,如果获取不到锁就死等
  • tryLock(超时时间):加锁,如果获取不到锁,等待一定的时间之后就放弃加锁
  • unlock():解锁

代码:

import java.util.concurrent.locks.ReentrantLock;

public class Demo30 {
    public static void main(String[] args) {
        ReentrantLock locker = new ReentrantLock(true);

        try {
            // 加锁
            locker.lock();
        } finally {
            // 解锁
            locker.unlock();
        }

    }
}

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

常见面试题:
synchronized 和 ReentrantLock 的区别?

区别 = 缺点 + 优势! 参考以上内容~~

三、原子类

原子类内部用的是 CAS 实现,所以性能要比加锁实现 i++ 高很多。原子类有以下几个:
(Java里已经封装好了,可以直接来使用~)
在这里插入图片描述

代码:
以 AtomicInteger 举例,常见方法有:

addAndGet(int delta);   i += delta;
decrementAndGet(); --i;
getAndDecrement(); i--;
incrementAndGet(); ++i;
getAndIncrement(); i++;
import java.util.concurrent.atomic.AtomicInteger;

public class Demo31 {
    public static void main(String[] args) throws InterruptedException {
        AtomicInteger count = new AtomicInteger(0);
//                // 相当于 ++count
//                count.incrementAndGet();
//                // 相当于 count--
//                count.getAndDecrement();
//                // 相当于 --count
//                count.decrementAndGet();

        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 50000; i++) {
                // 相当于 count++
                count.getAndIncrement();
            }
        });
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 50000; i++) {
                // 相当于 count++
                count.getAndIncrement();
            }
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();

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

四、线程池

线程池 (通俗易懂),博客链接~~

五、信号量Semaphore

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

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

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

信号量可以视为是一个更广义的锁;锁就是一个特殊的信号量 (可用资源只有1的信号量)!
把互斥锁也看作是计数为1的信号量 (取值只有1和0,也叫做二元信号量)

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

当需求中有多个可用资源的时候,就要记得使用信号量!!!(目的也是为了控制线程安全~~)

代码示例:

import java.util.concurrent.Semaphore;

public class Demo32 {
    public static void main(String[] args) throws InterruptedException {
        // 构造的时候需要指定初始值, 计数器的初始值. 表示有几个可用资源
        Semaphore semaphore = new Semaphore(4);
        // 这是 P 操作, 申请资源, 计数器 - 1
        semaphore.acquire();
        System.out.println("P 操作");
        semaphore.acquire();
        System.out.println("P 操作");
        semaphore.acquire();
        System.out.println("P 操作");
        semaphore.acquire();
        System.out.println("P 操作");
        semaphore.acquire();
        System.out.println("P 操作");
        // 这是 V 操作, 释放资源, 计数器 + 1
        semaphore.release();
    }
}

信号量这个概念是荷兰数学家迪杰斯特拉提出来的,"求图的最短路径"就是他提出的!
P、V 就是荷兰语中的申请和释放的首字母~~


例题: 编写代码实现两个线程增加同一个变量 (使用 Semphore 来控制线程安全)

import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;

public class ThreadDemo5 {
    public static void main(String[] args) throws InterruptedException {
        Semaphore semaphore = new Semaphore(1);
        AtomicInteger count = new AtomicInteger();

        Thread t1 = new Thread(() -> {
            try {
                semaphore.acquire();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            for (int i = 0; i < 100; i++) {
                count.getAndIncrement();
            }
            semaphore.release();
        });

        Thread t2 = new Thread(() -> {
            try {
                semaphore.acquire();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            for (int i = 0; i < 100; i++) {
                count.getAndIncrement();
            }
            semaphore.release();
        });

        t1.start();
        t2.start();
        t1.join();
        t2.join();

        System.out.println("count:" + count);
    }
}

六、闭锁CountDownLatch

类似于一个跑步比赛:
在这里插入图片描述
在这里插入图片描述
代码:

import java.util.concurrent.CountDownLatch;

public class Demo {
    public static void main(String[] args) throws InterruptedException {
        // 有 10 个选手参加了比赛
        CountDownLatch countDownLatch = new CountDownLatch(10);
        for (int i = 0; i < 10; i++) {
            // 创建 10 个线程来执行一批任务.
            Thread t = 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();
            });
            t.start();
        }

        // await 是进行阻塞等待. 会等到所有的选手都撞线之后, 才解除阻塞
        countDownLatch.await();
        System.out.println("比赛结束!");
    }
}

七、线程安全的集合类

原来的集合类,大部分都不是线程安全的。

Vector, Stack, HashTable, 是线程安全的(不建议用), 其他的集合类不是线程安全的!

7.1 多线程环境使用 ArrayList

1)自己使用同步机制 (synchronized 或者 ReentrantLock)

2)Collections.synchronizedList(new ArrayList);

synchronizedList 是标准库提供的一个基于 synchronized 进行线程同步的 List,
synchronizedList 的关键操作上都带有 synchronized
(套了一层壳,壳上加锁了)

3)使用 CopyOnWriteArrayList

CopyOnWrite容器即写时拷贝的容器。

  • 当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,
  • 添加完元素之后,再将原容器的引用指向新的容器。

这样做的好处是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。
所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。
优点:
在读多写少的场景下,性能很高,不需要加锁竞争
缺点:
1.占用内存较多
2.新写的数据不能被第一时间读取到

在这里插入图片描述

这种写时拷贝的思想很多地方都会用到!一个典型的,显卡给显示器渲染画面,也是类似的操作~~

7.2 多线程环境使用队列

1)ArrayBlockingQueue

基于数组实现的阻塞队列

2)LinkedBlockingQueue

基于链表实现的阻塞队列

3)PriorityBlockingQueue

基于堆实现的带优先级的阻塞队列

4)TransferQueue

最多只包含一个元素的阻塞队列

7.3 多线程环境使用哈希表

HashMap 本身不是线程安全的。
在多线程环境下使用哈希表可以使用:

  • Hashtable (不推荐使用,是属于无脑给各种方法加synchronized)
  • ConcurrentHashMap (推荐使用!背后做了很多的优化策略~~)

HashTable是官方明确提出不建议使用的!上古时期就有了,当时设计得不是很好,确实现在就没啥优势~~

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/85715.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【AI with ML】第 1 章 :TensorFlow 简介

&#x1f50e;大家好&#xff0c;我是Sonhhxg_柒&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流&#x1f50e; &#x1f4dd;个人主页&#xff0d;Sonhhxg_柒的博客_CSDN博客 &#x1f4c3; &#x1f381;欢迎各位→点赞…

python使用bs模块爬取小说数据

目录 一、BS模块介绍 二、分析页面架构 三、代码实现 四、结果展示 五、总结思路 一、BS模块介绍 Beautiful Soup提供一些简单的、python式的函数用来处理导航、搜索、修改分析树等功能。它是一个工具箱&#xff0c;通过解析文档为用户提供需要抓取的数据…

【云计算与大数据技术】Hadoop MapReduce的讲解(图文解释,超详细必看)

一、Hadoop MapReduce架构 MapReduce 是一种分布式计算框架,能够处理大量数据 ,并提供容错 、可靠等功能 , 运行部署在大规模计算集群中&#xff0c;MapReduce计算框架采用主从架构&#xff0c;由 Client、JobTracker、TaskTracker组成 Client的作用 用户编写 MapReduce程序…

Debye-Wolf积分计算器

摘要 众所周知&#xff0c;Debye-Wolf积分可用于以半解析方式计算焦平面附近的矢量场。Debye-Wolf积分通常用作分析高数值孔径显微镜成像情况的基本工具。它是基于理想模型&#xff0c;因此不需要待求镜头精确规格的知识。该用例将解释如何在VirtualLab Fusion中使用Debye-Wolf…

Guitar Pro8更新了多达30项功能优化

很多人在听到Guitar Pro这个名词时&#xff0c;本能反应就是跟吉他有关的软件吧&#xff0c;但是具体是什么样子&#xff0c;有什么功能我们却不一定知道的那么详细&#xff0c;下面呢&#xff0c;我们就来详细的介绍下Guitar Pro这款软件。 Guitar Pro是初学作曲&#xff0c;特…

openlayers百万级和千万级数据量的矢量切片在渲染过程中的技术难点解析

目录1 前言2 数据介绍3 切片方案及技术难点3.1 大数据量图层矢量切片无法正确返回结果3.2 矢量切片标注过程中标注拥挤3.3 矢量切片每一块切片都进行了标注&#xff0c;形成了冗余3.4 矢量切片标注的随机性会造成时而在中央&#xff0c;时而在边界上3.5 特殊样式的制作3.6 图层…

Jmeter(九):jmeter_逻辑控制器_事务控制器HTTP Cookie管理器

Jmeter(19)&#xff1a;jmeter_逻辑控制器_事务控制器 事务&#xff1a; 性能测试中&#xff0c;事务指的是从端到端&#xff0c;一个完整的操作过程&#xff0c;比如一次登录、一次 筛选条件查询&#xff0c;一次支付等&#xff1b;技术上讲&#xff1a;事务就是由1个或多个…

C# Spire.Xls将DataTable导出到Excel并加密

背景需求&#xff1a; 页面展示信息列表&#xff0c;需要将数据导出到Excel并加密sheet文件和工作簿结构。 代码思路&#xff1a;1.将数据转换为dataTable&#xff0c;2.将dataTable导出到Excel&#xff0c;3.处理加密。 文章目录引入必要的dll文件1.将数据转换为DataTable2.将…

Python英文词频统计(哈姆雷特)程序示例

今天继续给大家介绍Python相关知识&#xff0c;本文主要内容是Python英文词频统计程序示例&#xff0c;主要是对英文文本——《哈姆雷特》进行分词。 一、英文文本词频统计思路 想要对《哈姆雷特》进行英文单词词频统计&#xff0c;那么我们首先需要拿到《哈姆雷特》的原文&a…

“笨鸟”逆风飞:一场荣耀的高端越峰征程

2020年的11月17日&#xff0c;荣耀被迫“成年”。 “那一天都是在忙碌过程中度过的&#xff0c;有很多人要去沟通&#xff0c;有很多资产的分割……”&#xff0c;在12月12日央视网财经的访谈节目《云顶对话》中&#xff0c;荣耀CEO赵明首次向外袒露心迹&#xff0c;转身成为对…

C++11标准模板(STL)- 算法(std::max)

定义于头文件 <algorithm> 算法库提供大量用途的函数&#xff08;例如查找、排序、计数、操作&#xff09;&#xff0c;它们在元素范围上操作。注意范围定义为 [first, last) &#xff0c;其中 last 指代要查询或修改的最后元素的后一个元素。 返回各给定值中的较大者 …

构建供销一体化电商交易体系,数商云S2B2B系统实现锂电池企业全面转型升级

近年来&#xff0c;得益于新能源汽车和储能产业的快速发展&#xff0c;相关支撑技术和产业也得到空前关注&#xff0c;锂电池就是其中一员。我国动力电池装机量达到92GWH&#xff0c;同比增长169%&#xff0c;超过全球平均增速&#xff0c;占据着全球近一半的电池装机量。 新能…

Qt 蓝牙库基础

1.目的 使用QT自带的蓝牙功能库进行学习开发&#xff0c;了解蓝牙设备的工作原理和使用流程&#xff0c;目的搭建可以自由控制的蓝牙app&#xff0c;为以后的学习生活做基础。 2.本地设备 采用自己vivo Y53的手机作为实验对象。 使用到的QT蓝牙库的类名&#xff1a; QBlue…

PIC 16F18系列单片机开发 IO口配置与stm32的区别

目录 前言 PIC单片机配置字 电路需要IO口来发挥作用 寄存器和特殊功能寄存器 I/O口 I/O口和单片机内部寄存器之间的对应关系 前言 由于在之前学习嵌入式IDE时候都是直接接触到的是STM32 ARM内核的32位看偏激&#xff0c;现在开发使用的是美国微芯公司推出的PIC系列的单片…

【云原生】kubeadm部署k8s集群

内容预知 架构说明 1.环境准备 2.所有节点安装docker 3. 所有节点安装kubeadm&#xff0c;kubelet和kubectl 4. 部署 Dashboard 5. 安装 部署与k8s集群对接的Harbor仓库 内核参数优化方案 架构说明 前面的文章中&#xff0c;我们采用了二进制包搭建出的k8s集群&#xff…

[附源码]计算机毕业设计动漫电影网站Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; Springboot mybatis MavenVue等等组成&#xff0c;B/S模式…

电子采购软件如何提高企业效率?

企业采购致力于提高生产力和效率&#xff0c;并实现更高的职能和跨职能支出的可见性。电子采购软件解决方案帮助企业实现其目标并做得更多&#xff0c;因此成为了具有前瞻性的采购领导人的明智选择。 以下是使用电子采购软件的四个主要优势&#xff1a;1、降低成本 降低成本是电…

python包管理pip常见操作

一、pip安装 暂略 二、pip常用命令 1、显示版本和路径 pip --version 2、获取帮助 pip --help 3.升级 pip pip install -U pip 4.安装资源包 pip install SomePackage # 最新版本 pip install SomePackage1.0.4 # 指定版本 pip install SomePackage&g…

微服务框架 SpringCloud微服务架构 分布式事务 38 动手实践 38.8 案例实现TCC 模式【实现】

微服务框架 【SpringCloudRabbitMQDockerRedis搜索分布式&#xff0c;系统详解springcloud微服务技术栈课程|黑马程序员Java微服务】 分布式事务 文章目录微服务框架分布式事务38 动手实践38.8 案例实现TCC 模式【实现】38.8.1 声明TCC 接口38 动手实践 38.8 案例实现TCC 模…

第34篇 网络(四)FTP(二)

导语 前面讲述了一个最简单的FTP客户端程序的编写&#xff0c;这一节我们将这个程序进行扩展&#xff0c;使其可以浏览并能下载服务器上的所有文件。 环境&#xff1a;Windows Xp Qt 4.8.5QtCreator 2.8.0 目录 一、修改界面二、功能实现 正文 一、修改界面 我们删除了T…