多线程进阶(下)

2023-10-26

目录

一.JUC

二.线程安全的集合类

 三.死锁


一.JUC

这里的juc指的是java.util.concurrent(并发,多线程相关的),一个标准库中的类,下面是JUC里面的常见类:

Callable

这是一个interface,也是一种创建线程的方式,之前学的Runnable也是,但是这个不太适合让线程计算出一个结果这样的代码,例如创建一个线程让线程计算从1到1000相加的和,如果基于runnable实现的话,就会比较麻烦,还会陷入使线程陷入阻塞状态,而使用callable接口就可以很好的解决runnable这样不方便返回结果的情况,可以看一下callable的具体使用:


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

public class Demo28 {
    public static void main(String[] args) {
        Callable<Integer> callable = new Callable<Integer>() {//使用callable来描述一个任务,方便返回值
            @Override
            public Integer call() throws Exception {
                int sum = 0;
                for (int i = 0; i <= 1000; i++) {
                    sum += i;
                }
                return sum;//这就可以返回值了
            }
        };
        //为了让线程执行callable里面的任务,使用构造方法还不行,还需要一个辅助的类
        FutureTask<Integer> task = new FutureTask<>(callable);
        //这就相当于是小票单号一样,因为任务可能不止一个,因此需要这样的"小票"来对应相关的任务,然后凭借这样的"小票"就可以取出任务,对上号,然后可以返回结果
        //创建线程来完成这里的计算任务
        Thread t = new Thread(task);//Thread并没有一个构造方法直接传入callable,因此需要上面的辅助类
        t.start();
        //如果线程没有执行结束,get就会阻塞,一直阻塞到任务完成了结果算出来,才会得到结果
        try {
            System.out.println(task.get());//凭小票得到结果
        } catch (InterruptedException e) {//阻塞过程被打断的异常
            e.printStackTrace();
        } catch (ExecutionException e) {//任务执行过程中出异常
            e.printStackTrace();
        }
    }
}

通过这样的创建线程方式,就会比runnable的简单不少,效率也有所提高

ReentrantLock

这个翻译过来是可重入锁,先介绍一下基本用法:


import java.util.concurrent.locks.ReentrantLock;
public class Demo29 {
    public static void main(String[] args) {
        ReentrantLock locker = new ReentrantLock();
        //加锁
        locker.lock();
        //如果这里有抛出异常的话,后面的unlock很有可能执行不到,然后导致死锁,因此这里一般的写法就是unlock放到finally里面保证一定能执行到
        //解锁
        locker.unlock();
        //这里将加锁和解锁区分开了
    }
}

 然后剩下的其他用法就和synchronized差不多类似了,都是当多个线程竞争同一个锁的时候就会阻塞

而我们之前学的synchronized也是可重入锁,那么和synchronized的区别是什么呢?

区别:

1.synchronized是一个关键字(背后的逻辑是JVM内部实现的),而ReentrantLock是一个标准库中的类(背后的逻辑是Java代码写的类)

2.synchronized不需要手动释放说,出了代码块,就会自己释放,而ReentrantLock必须要手动释放锁,而且不能忘记释放,否则会死锁

3.synchronized如果竞争锁失败,就会陷入阻塞等待状态,但是ReentrantLock除了阻塞等待这个之外,trylock,失败了就会直接返回(不会一直阻塞死等,或者等待多久,之后就可以会去做其他的事情,这就增加了锁的灵活性)

4.synchronized是一个非公平锁,而ReentrantLock提供了非公平锁和公平锁两个版本,在构造函数中,通过参数来指定是公平锁还是非公平锁,不写参数默认就是非公平锁

ReentrantLock locker1 = new ReentrantLock(true);//公平锁

这就是一个公平锁了

5.基于synchronized衍生出来的等待机制,是wait,notify,功能是相对有限的,

而基于ReentrantLock衍生出来的等待机制,是Condition类(条件变量),这个功能更丰富一些,这里了解一下就可以!

原子类也是这个类下面(import java.util.concurrent.atomic.AtomicInteger)的具体可以看我前面的文章:多线程进阶(上)_栋zzzz的博客-CSDN博客

线程池也是这个类下面(import java.util.concurrent.Executors)的具体可以看我前面的文章:

多线程案例_栋zzzz的博客-CSDN博客

semaphore(信号量)

这是一个更广义的锁,可以说锁就是信号量的一种特殊情况,叫做"二元信号量"

举个例子:比如一个停车场内的停车位一般都是右确定数目的,因此停车场入口一般都有标记当前空闲多少个车位,如果有车开进去,车位数就-1,如果有车开出去,车位数就+1,此时我们就认为这个标记牌就是信号量,描述了可用资源(车位)的个数,每次申请一个可用资源,计数器就-1(称为P操作),每次释放一个可用资源没计数器就+1(称为V操作),当信号量的计数为0了,再次进行P操作就会进入阻塞等待状态,而P操作在代码中的方法是acquire(申请),V操作是release(释放)

锁就可以视为"二元信号量",锁的可用资源就一个,计数器要么为1,要么为0,而信号量就把锁推广到了一般情况,可用资源更多的时候该如何处理:

import java.util.concurrent.Semaphore;
public class Demo30 {
    public static void main(String[] args) throws InterruptedException {
        //初始化的值表示可用资源有4个
        Semaphore semaphore = new Semaphore(4);
        //申请资源,P操作
        semaphore.acquire();//还可用申请或释放多个资源(加参数),当资源申请完了再次申请的话就会曹成阻塞效果,释放也是一样的
        //释放资源,V操作
        semaphore.release();
    }
}

CountDownLatch

可以理解为一个"终点线"一样,假设有一场百米赛跑,而这个就可以理解为是一个终点线,当所有的选手都冲过终点线的时候,这场比赛才会结束!这样的场景,在开发中也是存在的,例如多线程下载(把一个文件拆成多个部分,每个线程负责下载其中的一个部分,当所有的线程都完成自己的下载才算下载完成,这样就可以提高整个文件的下载效率),利用代码还原一下这场百米赛跑:

CountDownLatch的基本方法:

countDown:给每个线程里面去调用,就表示到达终点了

await:是给等待线程去调用,当所有的线程都到达终点了,await就从阻塞状态中返回,就表示任务完成了

import java.util.concurrent.CountDownLatch;
public class Demo31 {
    public static void main(String[] args) {
        //构造函数的参数表示这里有几个选手参赛
        CountDownLatch latch = new CountDownLatch(10);
        for (int i = 0; i < 10; i++) {
            Thread t  = new Thread(()->{//多个线程调度是随机的
                try {
                    Thread.sleep(3000);
                    System.out.println(Thread.currentThread().getName() + "到达终点");
                    latch.countDown();//表示这个选手到达终点
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            });
            t.start();
        }
        //裁判需要等待所有的选手都通过终点线才会结束这场比赛
        //当这些线程没有执行完的时候,await就会是阻塞状态,所有的线程执行完了,await才会返回
        try {
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("比赛结束");
    }
}

调用countDown的次数和构造参数的个数一致时,就会返回await

二.线程安全的集合类

像Vector,HashTable,Stack这样的几个都是线程安全的,那么在多线程环境下如何使用不安全的ArrayList:

1.可以自主给关键步骤加锁

2.可以使用标准库里面的synchronizedList方法,这是基于synchronized进行线程同步的List,这里面的关键步骤都会锁:Collection.synchronized(new ArrayList),但是这个就不像自主加锁那样的灵活性了

3.使用CopyOnWriteArrayList,这是写时拷贝(在修改的时候会创建一份副本出来,就把ArrayList复制到副本,然后修改副本里面的内容,修改完成之后再让副本转正,还原回去(这样做的好处就是修改的同事对于读操作是没有任何影响的,读的时候优先读旧的版本))的容器,这个适合于读多写少的情况,也适合数据小的情况(更新配置数据,经常会用到这种类似的操作)

多线程使用队列,类似于LinkedBlockingQueue这样的,我前面都是介绍过的这些线程安全的队列,这里就不细说了

多线程环境下使用哈希表

HashMap本身是线程不安全的,这是不能直接在多线程环境下使用的,因此我们有两种解决方案:

1.HashTable(不推荐)

HashTable就是主要给关键方法加锁

这是直接针对方法加锁,也就是针对this加锁,当有多个线程来访问HashTable的时候,无论啥样的操作,都会产生锁竞争,这样的设计就会导致锁竞争的概率非常大,效率就会比较低! 

2.ConcurrentHashMap(推荐)

优点:

1.ConcurrentHashMap就是将每一个链表的头结点就加上一把锁,锁的竞争就会减小,效率也会提高

 2.另外ConcurrentHashMap只是针对写操作加锁了,读操作是没有加锁的,只是使用了volatile,避免内存可见性的问题

3.ConcurrentHashMap中更广泛的使用了CAS,进一步提高了效率(比如维护size的操作)

4.ConcurrentHashMap针对扩容进行了巧妙的化整为零(如果元素多了,链表长度变长,就会影响哈希表的效率,因此就需要扩容,而对于之前的扩容操作就会创建一个更大的数组,把之前旧的元素全部搬运过去,这样的操作是非常耗时的,HashTable就是这样做,ConcurrentHashMap就会每次操作只搬运一点点,通过多次的操作完成整个搬运的过程,会同事维护一个新的HashMap和一个旧的,查找的时候急需要查找旧的也要查找新的,插入的时候只插入新的,直到搬运完成之后,就会销毁旧的HashMap)

 三.死锁

 最后就是死锁,这里就不过多介绍了,具体可以看我前面关于synchronized里面介绍的死锁内容,各种死锁都包含了:线程加锁关键字_栋zzzz的博客-CSDN博客

以上就是所有内容啦,感谢支持!!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

多线程进阶(下) 的相关文章

  • PDFBox 更改线条颜色

    我使用 pdfbox 1 8 5 并尝试使用绘制彩色线PDPageContentStream drawLine 我尝试过PDPageContentStream setNonStrokingColor 255 0 0 但它不起作用 我也没有找
  • 为什么要使用继承? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 如何使 Java 中的自定义泛型类型链表排序?

    我正在用 java 编写自己的泛型链表 而不是使用 java 集合链表 链表的add方法由以下代码组成 public void add T item int position Node
  • 通过selenium在firefox中打开私有模式

    实际上我想通过selenium打开firefox浏览器的隐身 私密模式 但每次我尝试时都是以正常模式打开firefox 经过一番谷歌搜索后 我得到了这段代码 我用它通过 selenium 在 Firefox 中打开私有模式 但它不起作用 F
  • Spring Data Redis JedisConnectionException:流意外结束

    雷迪斯3 0 5Spring数据Redis 1 3 6绝地武士2 6 3 我们的 Web 应用程序通过 pub sub 从 Redis 接收数据 还以键 值对的形式在 Redis 上执行数据读 写 读 写发生在监听线程 独立监控线程和htt
  • 根据使用频率随机生成字母?

    如何根据常用语音中的使用频率随机生成字母 任何伪代码都值得赞赏 但如果用 Java 实现就更棒了 否则 只需朝正确的方向戳一下就会有所帮助 注意 我不需要生成使用频率 我确信我可以很容易地查找到它 我假设您将频率存储为 0 到 1 之间的浮
  • 有没有一种简单的方法来加密java对象?

    我想将序列化对象存储到文件中 但我想对其进行加密 它不需要非常强的加密 我只是想要一些简单的东西 最好是最多几行代码 这会让其他人加载起来更加困难 我已经研究过 SealedObject 但关键是阻止我 理想情况下 我只想传递一个字符串作为
  • 如何处理过时的连接?

    我们的应用程序是一个 J2EE 应用程序 在 Websphere 6 1 上通过 Mainframe DB2 后端使用 Struts EJB Hibernate 最近已投入生产 我们收到过时的连接异常当用户第一次或有时登录应用程序时 此异常
  • 如何为 Android 应用实施 Google Play 许可? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 Locked 这个问题及其答案是locked help locked posts因为这个问题是题外话
  • jni.h:没有这样的文件或目录

    我一直在关注本教程 http www java tips org other api tips jni simple example of using the java native interface html 在第 5 步 我从 GCC
  • 在 Apache POI 4.0 中为 XSSFWorkbook 创建自定义颜色样式

    要在 Apache POI 3 7 及更低版本中为 XSSFWorkbook 应用自定义颜色 可以执行以下操作 java awt Color c new java awt Color 1 2 3 XSSFCellStyle xcs xssf
  • 在具有重载构造函数的类中传递 null 时,首先调用哪个构造函数?

    下面是具有 3 个重载构造函数的 java 类 public class Test public Test Object i System out println Object invoked public Test String i Sy
  • ZipInputStream.getNextEntry() 如何工作?

    假设我们有这样的代码 File file new File zip1 zip ZipInputStream zis new ZipInputStream new FileInputStream file 假设您有一个包含以下内容的 zip
  • 在日志中显示 Spring 事务

    我为 spring 配置了事务支持 有什么方法可以记录事务以确保我正确设置所有内容 在日志中显示是查看正在发生的情况的好方法 in your log4j properties 对于替代记录器或 log4j 的 xml 格式 请查看文档 根据
  • 逐列读取 CSV 文件

    我想从多列 csv 文件中读取特定列 并使用 Java 在其他 csv 文件中打印这些列 有什么帮助吗 以下是我逐行打印每个标记的代码 但我希望只打印多列 csv 中的几列 import java io BufferedReader imp
  • Eclipse 中的预构建事件

    我有一个使用 jaxb 进行一些 xml 处理的项目 如何在 eclipse 中设置预构建事件以在构建项目之前执行 xjc 转到项目 gt 属性 gt 构建器 创建您自己的构建器并启用它 并在构建器的配置中启用 自动构建期间 等 如下所示
  • 自定义 lint 规则,确保不调用特定方法

    我想确保在我的 Android 应用程序 Java 和 Kotlin 代码中 中不会调用特定类的特定方法 假设 我有一个名为Bar有两种方法 allowed and disallowed 这是代码 package com public cl
  • 事务 TransactionImple ActionStatus.ABORTED 已回滚

    我实现了DTO业务从TomEE到JBoss的迁移 我有这个实体 NamedQueries NamedQuery name common plagebusiness plage getAllPlages query SELECT p FROM
  • 使用 libGDX 写入 Json

    我是 Json 和 libGDX 的新手 但我创建了一个简单的游戏 我想将玩家姓名及其分数存储在 Json 文件中 有没有办法做到这一点 我想创建一个 Json 文件Gdx files localStorage如果它不存在 如果存在 则向其
  • 为什么不建议将常量存储在单独的类中?

    有人告诉我 我在其他一些地方也看到过这种说法 不建议将常量存储在 Java 中的单独类中 以便在其他类中使用它们 但我没有看到任何地方为什么会这样 我不应该将它们存储在自己的接口 类中的原因是什么 我从 C 转到 Java 在 C 中我只想

随机推荐

  • Latex 中带左边大括号的方程组

    代码如下 documentclass article setlength textwidth 245 0pt usepackage CJK usepackage indentfirst usepackage amsmath begin CJ
  • 如何让ChatGPT你写一个短视频脚本

    很多网红博主以及各个领域的短视频博主都在使用的 AI编写视频脚本 效率直接提升20倍 很多自媒体平台对于ChatGPT的介绍很少 但是他们都在悄悄利用这个强大的AI来帮助处理工作 关于 如何利用ChatGPT编写视频脚本 这件事 我们今天就
  • 四行代码制作你的esp8266天气时钟——基于NodeMCU、OLED模块

    OLED 开学了 好闲呀 炸鸡 给你找个无休的工作 怎么样 ESP8266 物料 0 96OLED屏幕 esp8266 NodeMCU 开发板 杜邦线 可以自制PCB美化硬件组合 配置方法 四行代码 1 填上wifi或者热点的名称和密码 2
  • Apollo代码学习(三)—车辆动力学模型

    Apollo代码学习 车辆动力学模型 前言 车辆动力学模型 横向动力学 方向盘控制模型 总结 补充 2018 11 27 前言 接上一篇 Apollo代码学习 二 车辆运动学模型 主要参考资料仍是这三个 1 Rajamani R Vehic
  • Java学习心得

    Java学习心得 一 Java入门 Java是一门面向对象编程语言 不仅吸收了C 语言的各种优点 还摒弃了C 里难以理解的多继承 指针等概念 我初次接触java时 发现它和c语言有一些不同 不仅要定义类 还要搭建环境 我也是在同学的帮助下才
  • MySQL常见面试题(2023年最新)

    目录 前言 1 char和varchar的区别 2 数据库的三大范式 3 你了解sql的执行顺序吗 4 索引是什么 5 索引的优点和缺点 6 索引的类型 7 索引怎么设计 优化 8 怎么避免索引失效 也属于sql优化的一种 9 索引的数据类
  • JAVA多线程-线程安全问题

    一 CPU多核缓存架构 CPU分为三级缓存 每个CPU都有L1 L2缓存 但是L3缓存是多核公用的 CPU查找数据的顺序为 CPU gt L1 gt L2 gt L3 gt 内存 gt 硬盘 进一步优化 CPU每次读取一个数据 并不是仅仅读
  • 【NLP】使用 BERT 和 PyTorch Lightning 进行多标签文本分类

    大家好 我是Sonhhxg 柒 希望你看完之后 能对你有所帮助 不足请指正 共同学习交流 个人主页 Sonhhxg 柒的博客 CSDN博客 欢迎各位 点赞 收藏 留言 系列专栏 机器学习 ML 自然语言处理 NLP 深度学习 DL fore
  • html网页小插件,Html 小插件2

    调用google的JS翻译插件实现页面自动翻译功能 设置自己需要的配置生成如下代码放到自己站的页面头部 代码 开源Unity小插件CheatConsole 我们在开发游戏的过程中 通常都需要一些快捷的方式来进行一些非常规的测试 这些功能一般
  • SpringBoot启动Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactor

    SpringBoot启动Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactor异常原因总结 废话少说 上干货 原因一
  • 时代选出最重要 AI 100人,多位华人上榜

    来源 Time 链接 https time com collection time100 ai 编译 芯芯 编辑 靖宇 这是 AI 的时代 2022 年 没人能预料到 以大模型为代表的人工智能技术 成为这个时代独撑科技行业上行的力量 在这一
  • kubectl exec

    文章目录 kubectl exec 通过bash获得pod中某个容器的TTY 相当于登录容器 命令行 创建一个test文件 kubectl exec exec命令同样类似于docker的exec命令 为在一个已经运行的容器中执行一条shel
  • 模型的“参数”与“超参数”

    目录 前言 一 参数 与 超参数 二 模型训练与最终模型 三 参考文献 前言 起初由于团队项目临时需要 花了个一两天的时间直接仓促上手Machine Learning 最近回顾机器学习的模型评估与选择方面的内容时 才幡然发现在初识机器学习阶
  • 小程序发布后图片不显示问题

    本地图片能正常显示 发布后图片不显示 http 127 0 0 1 49287 pageframe static img apply 20 11 png 修改后 http 127 0 0 1 49287 pageframe static i
  • 获取table中的所有行中的td值,包括input值和select值等

    首先获取表对象document getElementById onceFeeTableId 然后通过循环可以获取所有的行 tableObj rows i 如果有需要可在循环里面再加一个循环 用来循环所有的列 HTML table class
  • 【Mysql 存储过程 Or 存储函数 傻傻分不清? 】

    MySQL的存储函数 自定义函数 和存储过程都是用于存储SQL语句的 但是什么时候用什么呢 是不是总是傻傻的分不清 本文来详细的讲一下存储函数 和存储过程 以后再也不会迷糊 存储函数 存储过程 一 异同点 二 存储函数 语法 三 存储过程
  • Ubuntu 20.04上安装和配置MySql5.7

    此博客作为学习笔记使用 仅供用于学习 勿用于任何非法用途 Ubuntu换源 ubuntu 20 04系统自带源直接安装是MySQL 8 0 我要安装MySQL 5 7的版本的所以先进行换源 1 1 备份原来的sorce文件 sudo cp
  • 深度之眼(四)——Python基本数据类型

  • 使用NPM发布uni-app,Vue自定义组件

    使用NPM发布uni app以及vue自定义组件 使用npm install 安装他人发布的组件平时用的比较多 操作也比较简单 最近自己捣鼓了一下 如何发布自己的自定义组件方便自己和他人使用npm install 安装 编写组件 第一步是本
  • 多线程进阶(下)

    目录 一 JUC 二 线程安全的集合类 三 死锁 一 JUC 这里的juc指的是java util concurrent 并发 多线程相关的 一个标准库中的类 下面是JUC里面的常见类 Callable 这是一个interface 也是一种