精华内容
下载资源
问答
  • Java高并发编程详解 PDF 下载
    千次阅读
    2021-02-12 18:03:32

    推荐序一

    推荐序二

    推荐序三

    推荐序四

    前言

    第一部分 多线程基础

    第1章 快速认识线程

    1.1 线程的介绍

    1.2 快速创建并启动一个线程

    1.3 线程的生命周期详解

    1.4 线程的start方法剖析:模板设计模式在Thread中的应用

    1.5 Runnable接口的引入以及策略模式在Thread中的使用

    1.6 本章总结

    第2章 深入理解Thread构造函数

    2.1 线程的命名

    2.2 线程的父子关系

    2.3 Thread与ThreadGroup

    2.4 Thread与Runnable

    2.5 Thread与JVM虚拟机栈

    2.6 守护线程

    2.7 本章总结

    第3章 Thread API的详细介绍

    3.1 线程sleep

    3.2 线程yield

    3.3 设置线程的优先级

    3.4 获取线程ID

    3.5 获取当前线程

    3.6 设置线程上下文类加载器

    3.7 线程interrupt

    3.8 线程join

    3.9 如何关闭一个线程

    3.10 本章总结

    第4章 线程安全与数据同步

    4.1 数据同步

    4.2 初识synchronized关键字

    4.3 深入synchronized关键字

    4.4 This Monitor和Class Monitor的详细介绍

    4.5 程序死锁的原因以及如何诊断

    4.6 本章总结

    第5章 线程间通信

    5.1 同步阻塞与异步非阻塞

    5.2 单线程间通信

    5.3 多线程间通信

    5.4 自定义显式锁BooleanLock

    5.5 本章总结

    第6章 ThreadGroup详细讲解

    6.1 ThreadGroup与Thread

    6.2 创建ThreadGroup

    6.3 复制Thread数组和ThreadGroup数组

    6.4 ThreadGroup操作

    6.5 本章总结

    第7章 Hook线程以及捕获线程执行异常

    7.1 获取线程运行时异常

    7.2 注入钩子线程

    7.3 本章总结

    第8章 线程池原理以及自定义线程池

    8.1 线程池原理

    8.2 线程池实现

    8.3 线程池的应用

    8.4 本章总结

    第二部分 Java ClassLoader

    第9章 类的加载过程

    9.1 类的加载过程简介

    9.2 类的主动使用和被动使用

    9.3 类的加载过程详解

    9.4 本章总结

    第10章 JVM类加载器

    10.1 JVM内置三大类加载器

    10.2 自定义类加载器

    10.3 本章总结

    第11章 线程上下文类加载器

    11.1 为什么需要线程上下文类加载器

    11.2 数据库驱动的初始化源码分析

    11.3 本章总结

    第三部分 深入理解volatile关键字

    第12章 volatile关键字的介绍

    12.1 初识volatile关键字

    12.2 机器硬件CPU

    12.3 Java内存模型

    12.4 本章总结

    第13章 深入volatile关键字

    13.1 并发编程的三个重要特性

    13.2 JMM如何保证三大特性

    13.3 volatile关键字深入解析

    13.4 本章总结

    第14章 7种单例设计模式的设计

    14.1 饿汉式

    14.2 懒汉式

    14.3 懒汉式+同步方法

    14.4 Double-Check

    14.5 Volatile+Double-Check

    14.6 Holder方式

    14.7 枚举方式

    14.8 本章总结

    第四部分 多线程设计架构模式

    第15章 监控任务的生命周期

    15.1 场景描述

    15.2 当观察者模式遇到Thread

    15.3 本章总结

    第16章 Single Thread Execution设计模式

    16.1 机场过安检

    16.2 吃面问题

    16.3 本章总结

    第17章 读写锁分离设计模式

    17.1 场景描述

    17.2 读写分离程序设计

    17.3 读写锁的使用

    17.4 本章总结

    第18章 不可变对象设计模式

    18.1 线程安全性

    18.2 不可变对象的设计

    18.3 本章总结

    第19章 Future设计模式

    19.1 先给你一张凭据

    19.2 Future设计模式实现

    19.3 Future的使用以及技巧总结

    19.4 增强FutureService使其支持回调

    19.5 本章总结

    第20章 Guarded Suspension设计模式

    20.1 什么是Guarded Suspension设计模式

    20.2 Guarded Suspension的示例

    20.3 本章总结

    第21章 线程上下文设计模式

    21.1 什么是上下文

    21.2 线程上下文设计

    21.3 ThreadLocal详解

    21.4 使用ThreadLocal设计线程上下文

    21.5 本章总结

    第22章 Balking设计模式

    22.1 什么是Balking设计

    22.2 Balking模式之文档编辑

    22.3 本章总结

    第23章 Latch设计模式

    23.1 什么是Latch

    23.2 CountDownLatch程序实现

    23.3 本章总结

    第24章 Thread-Per-Message设计模式

    24.1 什么是Thread-Per-Message模式

    24.2 每个任务一个线程

    24.3 多用户的网络聊天

    24.4 本章总结

    第25章 Two Phase Termination设计模式

    25.1 什么是Two Phase Termination模式

    25.2 Two Phase Termination的示例

    25.3 知识扩展

    25.4 本章总结

    第26章 Worker-Thread设计模式

    26.1 什么是Worker-Thread模式

    26.2 Worker-Thread模式实现

    26.3 本章总结

    第27章 Active Objects设计模式

    27.1 接受异步消息的主动对象

    27.2 标准Active Objects模式设计

    27.3 通用Active Objects框架设计

    27.4 本章总结

    第28章 Event Bus设计模式

    28.1 Event Bus设计

    28.2 Event Bus实战——监控目录变化

    28.3 本章总结

    第29章 Event Driven设计模式

    29.1 Event-Driven Architecture基础

    29.2 开发一个Event-Driven框架

    29.3 Event-Driven的使用

    29.4 本章总结

    更多相关内容
  • 我是全网最硬核的高并发编程作者,CSDN最值得关注博主,全网最硬核的基于可视化的多数据源数据异构中间件作者,也许现在就是,也是不久的将来就是,大家同意吗?

    原创不易,小伙伴们给个一键三连呀~~

    大家好,我是冰河~~

    正如文章标题所言,我是全网最硬核的高并发编程作者,CSDN最值得关注博主,全网最硬核的基于可视化的多数据源数据异构中间件作者,也许现在就是,也是不久的将来就是。

    为啥这样说?因为我写了一本全网首部完全免费并且开源的,全方位涵盖源码分析、基础案例、实战案例、面试和系统架构的《深入理解高并发编程》电子书,发行四个月全网累计下载17万+。 而且,我在CSDN上写了近1700篇超硬核原创技术文。我开源的基于可视化的多数据源数据异构中间件mykit-data超30家公司采用,承担着其核心业务数据的全量、增量(定时、实时)同步,经受住了生产环境中大数据量场景下的全量、增量(定时、实时)同步的考验。 其他的,且听我慢慢道来。

    有很多小伙伴是最近才关注我的,对我的了解不多,那我就先厚着脸皮来个自我介绍吧。

    自我介绍

    我是冰河,《海量数据处理与大数据技术实战》,《MySQL技术大全:开发、优化与运维实战》畅销书作者,CSDN博客专家,mykit系列开源框架作者,基于最终消息可靠性的开源分布式事务框架mykit-transaction-message作者,基于可视化多数据源数据异构中间件mykit-data作者。多年来致力于分布式系统架构、微服务、分布式数据库、分布式事务与大数据技术的研究。在高并发、高可用、高可扩展性、高可维护性和大数据等领域拥有丰富的架构经验。对Hadoop,Storm,Spark,Flink等大数据框架源码进行过深度分析,并具有丰富的实战经验,目前在研究云原生领域。

    我的CSDN唯一博客主页:https://binghe.blog.csdn.net/ ,我在CSDN上更新了将近1700篇硬核原创技术文。

    在这里插入图片描述
    我在CSDN发表的博文内容涵盖架构、研发、后端、前端、测试、运维、渗透、大数据、云计算、云原生等多个领域。

    在这里插入图片描述

    我也是《海量数据处理与大数据技术实战》,《MySQL技术大全:开发、优化与运维实战》两本畅销书的作者。而且写这两本畅销书我只用了5个月时间,一本500多页,一本700多页,从基础、开发、优化、运维到架构,全程实战干货。我个人觉得我可能是业界写书最快的作者(应该就是最快的吧,你们说呢?)。

    在这里插入图片描述

    同时,业余时间我也维护着一些个人的开源项目,每个开源项目都是我个人在业余时间持续更新和维护,比较知名的有:

    GitHub:
    文章收录:
    https://github.com/sunshinelyz/technology-binghe
    可视化多数据源异构中间件:
    https://github.com/sunshinelyz/mykit-data
    分布式序列号(ID)生成器:
    https://github.com/sunshinelyz/mykit-serial
    基于最终消息可靠性分布式事务框架:
    https://github.com/sunshinelyz/mykit-transaction-message
    精准延迟任务与消息队列框架:
    https://github.com/sunshinelyz/mykit-delay

    Gitee:
    文章收录:
    https://gitee.com/binghe001/technology-binghe
    可视化多数据源异构中间件:
    https://gitee.com/binghe001/mykit-data
    分布式序列号(ID)生成器:
    https://gitee.com/binghe001/mykit-serial
    基于最终消息可靠性分布式事务框架:
    https://gitee.com/binghe001/mykit-transaction-message
    精准延迟任务与消息队列框架:
    https://gitee.com/binghe001/mykit-delay

    其中,mykit-data可视化多数据源异构中间件已经在超过30家公司的生产环境使用,经受住了生产环境高并发,大流量下的数据全量、增量(定时、实时)同步的考验。

    好了,自我介绍完毕,小伙伴们如果对这些开源项目感兴趣,自己可以下载源码研究下,遇到不懂的问题可以提Issues,也可以在CSDN上私信我,我看到后都会回复大家。

    接下来,我们就聊聊为何我是全网最硬核的高并发编程作者。

    写作背景

    2020年疫情期间,大部分企业都实行居家办公的策略,而我,也在家里办公。这就省去了每天坐公交上下班的时间,无形当中,节省了很多时间,也就意味着有了更多的时间来自由支配。那多出来的时间干啥?想来想去,还是写一些关于高并发编程的技术文章吧,因为在网上公开的关于高并发编程的知识要么很零散,要么就是收费的,根本无法满足我对高并发编程的需求。 怎么办?既然网上没有,那我就自己写吧。

    艰难的写作过程

    整个写作过程其实还是挺艰难的,需要 梳理写作思路,画脑图,看源码,画流程图,写作,排版 等一系列的流程。一篇文章从构思到写作完成真的要付出很多心血。

    开始最难的还是确定【精通高并发系列】专栏的范围,到底要写哪些内容,内容到底要写到何种深度,想来想去,我画了一个简单的脑图,先把要写的并发基础知识写出来。

    开始,我就先写并发编程的三大核心问题:协作、互斥和分工。按照脑图深入并发编程的类库、框架原理和源码。尽管很难,那段时间还是坚持着日更的节奏。每天几乎都是 5点半起床写文,8点吃早饭,9点按时在家上班打卡,下午6点在家下班打卡,吃晚饭。然后晚上7点钟又开始写文,日复一日的坚持。

    写到后面,内容就不止这些了。加入了 源码分析、基础案例、实战案例、面试、系统架构 等关于高并发的硬核知识,真正的实现从原理、源码到实战、面试与系统架构的全方位高并发技术。

    给大家看下就【精通高并发系列】一个专题我写的文章底稿。

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    几乎涉及的所有图片都是我自己手动画出来的。

    在这里插入图片描述

    整理电子书

    写了这么多,后来有小伙伴给我提议,让我整理成电子书,方便他们查看。我觉得这个提议好,于是我又花了将近一周的时间把写的高并发相关的知识整理成了电子书。整理完电子书,又开始纠结了,这本书叫啥名字呢?最终取了一个读者给的名字《深入理解高并发编程(第1版)》。为啥是第1版?没错,就是第1版,因为后面我还要不断的迭代更新它,后续还会推出第2版、第3版、第4版等等。努力把它打造成为业界最厉害的免费开源的高并发编程电子书。

    整部书的结构不变,还是分为: 源码分析、基础案例、实战案例、面试、系统架构 五大部分。

    这部电子书一经问世,没想到短短的四个月时间,累计下载量就已突破17万+。目前下载量仍在持续上升。这部电子书为什么会这么火呢?它到底涵盖哪些内容呢?接下来,我们一探究竟。

    关于电子书

    《深入理解高并发编程(第1版)》这部PDF大部分内容来自冰河的CSDN博客【精通高并发系列】专栏,整体大约36W字,优化排版后共计315页。

    在这里插入图片描述

    涵盖:源码分析篇、基础案例篇、实战案例篇、面试篇和系统架构篇。 这应该是全网最牛的免费开源的高并发编程电子书了吧! 全书结构如下所示。

    全书内容概览

    全书涵盖的内容如下所示。

    在这里插入图片描述

    书籍部分内容如下:

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    整部电子书从源码解析到系统架构一气呵成,系统架构篇则作为整部PDF的最后一个部分,在系统架构篇中,我在CSDN博客中挑选了两篇最具代表性的文章《高并发秒杀系统架构解密,不是所有的秒杀都是秒杀 !》和《高并发分布式锁架构解密,不是所有的锁都是分布式
    锁! !
    》。通过对秒杀系统架构和分布式锁架构的深入剖析,使得小伙伴们在更高的思维层次来深入理解高并发编程,并做到在实际项目中灵活运用。

    后续规划

    目前,我也在规划继续更新【精通高并发系列】文章,规划推出《深入理解高并发编程》第2版,第3版等等。我也重新细致的画了高并发编程的脑图。

    在这里插入图片描述

    注意:脑图建议放大后查看。

    后面要做的,就是持续更新文章啦。

    说了这么多,该如何下载这部四个月全网下载量突破17万+的电子书呢?别着急,冰河为你整理好,上传到CSDN啦。

    https://download.csdn.net/download/l1028386804/18209878

    也上传了一份到我的百度网盘。

    链接:https://pan.baidu.com/s/1f81RPMNOyIv3mgc-mTCBQg
    提取码:xhbq

    最后,还是标题那个问题,我是全网最硬核的并发编程作者,是最值得大家关注的CSDN博主,也是全网最硬核的基于可视化的多数据源数据异构中间件作者,大家同意吗?欢迎大家在文末留言交流~~

    如果文章对你有点帮助,小伙伴们给个一键三连~~

    展开全文
  • 分享课程——Java高并发编程,构建并发编程知识体系,提升面试成功率;带你构建完整的并发与高并发知识体系,一旦形成完整的知识体系,无论是跳槽面试还是开发,你都将是最快脱颖而出的那一个 本课程将结合大量图示...
  • │ 高并发编程第一阶段01讲、课程大纲及主要内容介绍.wmv │ 高并发编程第一阶段02讲、简单介绍什么是线程.wmv │ 高并发编程第一阶段03讲、创建并启动线程.mp4 │ 高并发编程第一阶段04讲、线程生命周期以及...
  • 多线程与高并发编程之基础知识(上)

    万次阅读 多人点赞 2018-10-01 21:48:25
    但是,想要使用好多线程这把利刃,还需要掌握好多线程编程的基础知识,从而做到得心应手地使用多线程进行性能程序的开发! 多线程的应用场景 程序中出现需要等待的操作,比如网络操作、文件IO等,可以利用...

    前言

    几乎所有的程序员都知道,现代操作系统进行资源分配的最小单元是进程,而操作系统进行运算调度的最小单元是线程,其实,在Linux中线程也可以看作是一种轻量级的进程,那么线程是包含于进程之中的,是进程中实际的运作单位;同一进程中的多个线程共用同一块内存空间,而不同的线程又拥有独立的栈内存用以存放线程本地数据;

    大家都知道,现在的计算机动辄就是多处理器核心的,而每一个线程同一时间只能运行在一个处理器上,那么如果程序采用单线程进行开发,那么就不能充分利用多核处理器带来的优势;所以为了充分利用多核处理器的资源来提高程序的执行性能,多线程编程变得越来越重要,比如对于计算密集型任务,使用一个线程可能需要100秒,但是,如果使用十个线程共同完成,那么需要的时间可能只有10秒左右;如果你是使用Java开发程序的,那么你很幸运,因为Java是内置多线程编程模型的;但是,想要使用好多线程这把利刃,还需要掌握好多线程编程的基础知识,从而做到得心应手地使用多线程进行高性能程序的开发!


    多线程的应用场景

    • 程序中出现需要等待的操作,比如网络操作、文件IO等,可以利用多线程充分使用处理器资源,而不会阻塞程序中其他任务的执行
    • 程序中出现可分解的大任务,比如耗时较长的计算任务,可以利用多线程来共同完成任务,缩短运算时间
    • 程序中出现需要后台运行的任务,比如一些监测任务、定时任务,可以利用多线程来完成

    自定义线程的实现

    处于实用的角度出发,想要使用多线程,那么第一步就是需要知道如何实现自定义线程,因为实际开发中,需要线程完成的任务是不同的,所以我们需要根据线程任务来自定义线程,JDK为我们的开发人员提供了三种自定义线程的方式,供实际开发中使用,来开发出符合需求的多线程程序!

    以下是线程的三种实现方式,以及对每种实现的优缺点进行分析,最后是对这三种实现方式进行总结;

    方式一:继承Thread类

    package com.thread;
    //通过继承Thread类实现自定义线程类
    public class MyThread extends Thread {
    	//线程体
        @Override
        public void run() {
            System.out.println("Hello, I am the defined thread created by extends Thread");
        }
        public static void main(String[] args){
            //实例化自定义线程类实例
            Thread thread = new MyThread();
            //调用start()实例方法启动线程
            thread.start();
        }
    }
    

    优点:实现简单,只需实例化继承类的实例,即可使用线程
    缺点:扩展性不足,Java是单继承的语言,如果一个类已经继承了其他类,就无法通过这种方式实现自定义线程

    方式二:实现Runnable接口

    package com.thread;
    public class MyRunnable implements Runnable {
        //线程体
        @Override
        public void run() {
            System.out.println("Hello, I am the defined thread created by implements Runnable");
        }
        public static void main(String[] args){
        	//线程的执行目标对象
            MyRunnable myRunnable = new MyRunnable();
            //实际的线程对象
            Thread thread = new Thread(myRunnable);
            //启动线程
            thread.start();
        }
    }
    

    优点:

    • 扩展性好,可以在此基础上继承其他类,实现其他必需的功能
    • 对于多线程共享资源的场景,具有天然的支持,适用于多线程处理一份资源的场景

    缺点:构造线程实例的过程相对繁琐一点

    方式三:实现Callable接口

    package com.thread;
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.FutureTask;
    
    public class MyCallable implements Callable<String> {
        @Override
        public String call() throws Exception {
            return "Hello, I am the defined thread created by implements Callable";
        }
        public static void main(String[] args){
            //线程执行目标
            MyCallable myCallable = new MyCallable();
            //包装线程执行目标,因为Thread的构造函数只能接受Runnable接口的实现类,而FutureTask类实现了Runnable接口
            FutureTask<String> futureTask = new FutureTask<>(myCallable);
            //传入线程执行目标,实例化线程对象
            Thread thread = new Thread(futureTask);
            //启动线程
            thread.start();
            String result = null;
            try {
                //获取线程执行结果
                result = futureTask.get();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
            System.out.println(result);
        }
    }
    

    优点:

    • 扩展性好
    • 支持多线程处理同一份资源
    • 具备返回值以及可以抛出受检查异常

    缺点:

    • 相较于实现Runnable接口的方式,较为繁琐

    小结

    我们对这三种方式进行分析,可以发现:方式一和方式二本质上都是通过实现Runnable接口并重写run()方法,将接口实现类的实例传递给Thread线程类来执行线程体(run()方法中的实现),这里将Runnable接口实现类的实例作为线程执行目标,供线程Thread实例执行;对于方式三,其实也是这样的,由于Thread类只能执行Runnable接口实现类的执行目标,所以需要对Callable接口的实现类进行包装,包装成Runnable接口的实现类(通过实现了Runnable接口的FutureTask类进行包装),从而使得Thread类能够接收Callable接口实现类的实例,可见这里使用了适配器模式!

    综上所述,三种实现方式都存在着一个使用范式,即首先实现线程执行目标对象(包含线程所要执行的任务),然后将目标对象作为构造参数以实例化Thread实例,来获得线程!本质上都是实现一个线程体,由Thread来执行线程体,达到开启线程执行任务的效果!但是,三种实现方式各有优缺点,使用时,应该结合具体需求来选用合适的实现方式进行开发!


    线程的生命周期

    经过上面的代码演示,我们知道了线程如何实现,但是如果我们想要更好地使用线程,还需要对程序运行中线程的状态以及状态之间的转换(即线程的生命周期)有所了解,这样才能在多线程程序运行出现问题时,分析问题产生的原因,从而快速准确地定位并解决问题!

    首先,看一下Thread类中给出的关于线程状态的说明:

    	/**
         * 线程生命周期中的的六种状态
         * NEW:还没有调用start()的线程实例所处的状态
         * RUNNABLE:正在虚拟机中执行的线程所处的状态
         * BLOCKED:等待在监视器锁上的线程所处的状态
         * WAITING:等待其它线程执行特定操作的线程所处的状态
         * TIMED_WAITING:等待其它线程执行超时操作的线程所处的状态
         * TERMINATED:退出的线程所处的状态
         * 给定时间点,一个线程只会处于以下状态中的一个,这些状态仅仅是虚拟机层面的线程状态,并不能反映任何操作系统中线程的状态
         */
        public enum State {
            //还没有调用start()开启的线程实例所处的状态
            NEW, 
            //正在虚拟机中执行或者等待被执行的线程所处的状态,但是这种状态也包含线程正在等待处理器资源这种情况
            RUNNABLE,
            // 等待在监视器锁上的线程所处的状态,比如进入synchronized同步代码块或同步方法失败
            BLOCKED,
            // 等待其它线程执行特定操作的线程所处的状态;比如线程执行了以下方法: Object.wait with no timeout、Thread.join with no timeout、 LockSupport.park
            WAITING,
           // 等待其它线程执行超时操作的线程所处的状态;比如线程执行了以下方法: Thread.sleep、Object.wait with timeout
           //Thread.join with timeout、LockSupport.parkNanos、LockSupport.parkUntil
            TIMED_WAITING,
            //退出的线程所处的状态
            TERMINATED;
        }
    
    • 新建(New):当线程实例被new出来之后,调用start()方法之前,线程实例处于新建状态
    • 可运行(Runnable):当线程实例调用start()方法之后,线程调度器分配处理器资源之前,线程实例处于可运行状态或者线程调度器分配处理器资源给线程之后,线程实例处于运行中状态,这两种情况都属于可运行状态
    • 等待(Waitting):当线程处于运行状态时,线程执行了obj.wait()或Thread.join()方法、Thread.join、LockSupport.park以及Thread.sleep()时,线程处于等待状态
    • 超时等待(Timed Waitting):当线程处于运行状态时,线程执行了obj.wait(long)、Thread.join(long)、LockSupport.parkNanos、LockSupport.parkUntil以及Thread.sleep(long)方法时,线程处于超时等待状态
    • 阻塞(Blocked):当线程处于运行状态时,获取锁失败,线程实例进入等待队列,同时状态变为阻塞
    • 终止(Terminated):当线程执行完毕或出现异常提前结束时,线程进入终止状态

    线程的状态转换

    上面也提到了,某一时间点线程的状态只能是上述6个状态中的其中一个;但是,线程在程序运行过程中的状态是会发生变化的,由一个状态转变为另一个状态,那么下面给出线程状态转换图帮助我们清晰地理解线程的状态转变过程:
    在这里插入图片描述


    上面我们已经对线程的实现以及线程的状态有了较为清晰的认识,那么通过上述内容,我们也可以发现其实有很多方法,我们并没有详细地介绍,比如start()、yield()、wait()、notify()、notifyAll()、sleep()、join()等等,这些方法大多来源于JDK中Thread类这一关键的线程类中,下面结合Thread类的源码看一下,多线程编程中经常遇到的方法有哪些,以及这些方法的用途;

    线程类Thread源码

    实例同步方法:join()

        /**
         * 等待调用此方法的线程执行结束
         * @throws  InterruptedException 如果任何线程中断了当前线程,将会抛出此异常,同时将中断标志位清除
         */
        public final void join() throws InterruptedException {
            join(0);
        }
        /**
         * 最多等待millis毫秒,时间一到无论是否执行完毕,都会返回
         * 如果millis为0,那么意味着一直等到线程执行完毕才会返回
         * 此方法的实现是基于循环检测当前线程是否存活来判断是否调用当前实例的wait方法来实现的
         * @param  millis 等待时间
         * @throws  IllegalArgumentException 非法参数异常
         * @throws  InterruptedException 如果任何线程中断了当前线程,将会抛出此异常,同时将中断标志位清除
         */
        public final synchronized void join(long millis) throws InterruptedException {
            long base = System.currentTimeMillis();
            long now = 0;
            if (millis < 0) {
                throw new IllegalArgumentException("timeout value is negative");
            }
            if (millis == 0) {
                while (isAlive()) {
                    wait(0);
                }
            } else {
                while (isAlive()) {
                    long delay = millis - now;
                    if (delay <= 0) {
                        break;
                    }
                    wait(delay);
                    now = System.currentTimeMillis() - base;
                }
            }
        }
        /**
         * 线程执行结束之前最多等待millis毫秒nanos纳秒
         * 此方法基于循环判断isAlive返回值来决定是否调用wait方法来实现
         * 随着一个线程终止,将会调用notifyAll方法 
         * 所以建议不要在当前实例上调用 wait、 notify、 notifyAll
         */
        public final synchronized void join(long millis, int nanos)
                throws InterruptedException {
            if (millis < 0) {
                throw new IllegalArgumentException("timeout value is negative");
            }
            if (nanos < 0 || nanos > 999999) {
                throw new IllegalArgumentException(
                        "nanosecond timeout value out of range");
            }
            if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
                millis++;
            }
            join(millis);
        }
    

    中断方法以及检测中断方法和判活方法:

        /**
         * 中断当前线程
         * 如果当前线程阻塞在Object的wait()、wait(long)、wait(long, int),或者
         * join()、join(long)、join(long, int)以及sleep(long)、sleep(long, int)等方法
         * 那么将会清除中断标志位并受到一个中断异常
         * 非静态方法
         */
        public void interrupt() {
            if (this != Thread.currentThread())
                checkAccess();
            synchronized (blockerLock) {
                Interruptible b = blocker;
                if (b != null) {
                    interrupt0();           // Just to set the interrupt flag
                    b.interrupt(this);
                    return;
                }
            }
            interrupt0();
        }
        /**
         * 检测当前线程是否已经被中断,此方法会清除当前线程的中断标志
         * 也就是说,如果这个方法被连续调用两次,并且第一次调用之前,线程被中断过,那么第一次调用返回true,第二次返回false
         * @return  <code>true</code> 如果当前线程已经被中断,返回true,否则返回false     
         * 静态方法
         */
        public static boolean interrupted() {
            return currentThread().isInterrupted(true);
        }
        /**
         * 检测当前线程是否已经被中断,此方法不会清除当前线程的中断标志
         * 非静态方法
         */
        public boolean isInterrupted() {
            return isInterrupted(false);
        }
        /**
         * 根据参数值决定是否在判断中断标志位之后清除标志位
         * 实例方法
         */
        private native boolean isInterrupted(boolean ClearInterrupted);
        /**
         * 检测一个线程是否还存活,存活指的是已经启动但还没有终止
         * 实例方法
         */
        public final native boolean isAlive();
    

    结尾

    到此为止,本文已经对线程的使用场景、实现方式以及生命周期、状态转换过程以及线程类常用的方法进行了介绍,但是上面只是从概念上对线程的相关知识进行叙述,但是实际开发中,我们使用多线程是为了解决实际问题,比如如何实现多个线程共同完成一个耗时长的任务或者如何实现多个线程交互完成一个大型任务,在这些实际应用线程的过程中,会遇到问题,下面将在多线程与高并发编程基础知识(下)一文中给出多线程开发中,面临的问题以及对这些问题进行分析,并介绍常用的解决方案;最后,希望读者在阅读之后,如果发现文中出现不正确的地方还请指出,大家一同探讨和学习,共同成长^^!

    展开全文
  • 全网首个最全的免费开源的高并发电子书,内容涵盖源码分析、基础案例、实战案例、面试和系统架构,内含秒杀系统和分布式锁的完整架构过程,历时半年精打细磨,三个月下载量突破16万,无论是刚工作的初级程序员,还是...
  • JAVA高并发编程

    万次阅读 多人点赞 2018-05-07 09:30:26
    同步代码块的同步粒度更加细致,是商业开发中推荐的编程方式。可以定位到具体的同步位置,而不是简单的将方法整体实现同步逻辑。在效率上,相对更。 锁定临界对象 同步代码块在执行时,是锁定 object 对象。...

    synchronized 关键字

    可用来给对象和方法或者代码块加锁,当它锁定一个方法或者一个代码块的时候,同一时刻最多只有一个线程执行这段代码。可能锁对象包括: this, 临界资源对象,Class 类对象

    同步方法

    同步方法锁定的是当前对象。当多线程通过同一个对象引用多次调用当前同步方法时, 需同步执行。

    public synchronized void test(){
            System.out.println("测试一下");
        }

    同步代码块

    同步代码块的同步粒度更加细致,是商业开发中推荐的编程方式。可以定位到具体的同步位置,而不是简单的将方法整体实现同步逻辑。在效率上,相对更高。
    锁定临界对象
    同步代码块在执行时,是锁定 object 对象。当多个线程调用同一个方法时,锁定对象不变的情况下,需同步执行。

    public void test(){
            synchronized(o){
                System.out.println("测试一下");
            }
        }

    锁定当前对象

    public void test(){
            synchronized(this){
                System.out.println("测试一下");
            }
        }

    锁的底层实现

    Java 虚拟机中的同步(Synchronization)基于进入和退出管程(Monitor)对象实现。同步方法 并不是由 monitor enter 和 monitor exit 指令来实现同步的,而是由方法调用指令读取运行时常量池中方法的 ACC_SYNCHRONIZED 标志来隐式实现的。
    对象内存简图

    对象头:存储对象的 hashCode、锁信息或分代年龄或 GC 标志,类型指针指向对象的类元数据,JVM 通过这个指针确定该对象是哪个类的实例等信息。
    实例变量:存放类的属性数据信息,包括父类的属性信息
    填充数据:由于虚拟机要求对象起始地址必须是 8 字节的整数倍。填充数据不是必须存在的,仅仅是为了字节对齐
    当在对象上加锁时,数据是记录在对象头中。当执行 synchronized 同步方法或同步代码块时,会在对象头中记录锁标记,锁标记指向的是 monitor 对象(也称为管程或监视器锁) 的起始地址。每个对象都存在着一个 monitor 与之关联,对象与其 monitor 之间的关系有存在多种实现方式,如 monitor 可以与对象一起创建销毁或当线程试图获取对象锁时自动生成,但当一个 monitor 被某个线程持有后,它便处于锁定状态。
    在 Java 虚拟机(HotSpot)中,monitor 是由 ObjectMonitor 实现的。
    ObjectMonitor 中有两个队列,_WaitSet 和 _EntryList,以及_Owner 标记。其中_WaitSet 是用于管理等待队列(wait)线程的,_EntryList 是用于管理锁池阻塞线程的,_Owner 标记用于记录当前执行线程。线程状态图如下:

    这里写图片描述

    当多线程并发访问同一个同步代码时,首先会进入_EntryList,当线程获取锁标记后,
    monitor 中的_Owner 记录此线程,并在 monitor 中的计数器执行递增计算(+1),代表锁定,其他线程在_EntryList 中继续阻塞。若执行线程调用 wait 方法,则 monitor 中的计数器执行赋值为 0 计算,并将_Owner 标记赋值为 null,代表放弃锁,执行线程进如_WaitSet 中阻塞。若执行线程调用 notify/notifyAll 方法,_WaitSet 中的线程被唤醒,进入_EntryList 中阻塞,等待获取锁标记。若执行线程的同步代码执行结束,同样会释放锁标记,monitor 中的_Owner 标记赋值为 null,且计数器赋值为 0 计算。

    锁的种类

    Java 中锁的种类大致分为偏向锁,自旋锁,轻量级锁,重量级锁。
    锁的使用方式为:先提供偏向锁,如果不满足的时候,升级为轻量级锁,再不满足,升级为重量级锁。自旋锁是一个过渡的锁状态,不是一种实际的锁类型。
    锁只能升级,不能降级。

    重量级锁

    在锁的底层实现中解释的就是重量级锁。

    偏向锁

    是一种编译解释锁。如果代码中不可能出现多线程并发争抢同一个锁的时候,JVM 编译代码,解释执行的时候,会自动的放弃同步信息。消除 synchronized 的同步代码结果。使用锁标记的形式记录锁状态。在 Monitor 中有变量 ACC_SYNCHRONIZED。当变量值使用的时候, 代表偏向锁锁定。可以避免锁的争抢和锁池状态的维护。提高效率。

    轻量级锁
    过渡锁。当偏向锁不满足,也就是有多线程并发访问,锁定同一个对象的时候,先提升为轻量级锁。也是使用标记 ACC_SYNCHRONIZED 标记记录的。ACC_UNSYNCHRONIZED 标记记录未获取到锁信息的线程。就是只有两个线程争抢锁标记的时候,优先使用轻量级锁。
    两个线程也可能出现重量级锁。

    自旋锁
    是一个过渡锁,是偏向锁和轻量级锁的过渡。
    当获取锁的过程中,未获取到。为了提高效率,JVM 自动执行若干次空循环,再次申请锁,而不是进入阻塞状态的情况。称为自旋锁。自旋锁提高效率就是避免线程状态的变更。

    volatile 关键字

    变量的线程可见性。在 CPU 计算过程中,会将计算过程需要的数据加载到 CPU 计算缓存中,当 CPU 计算中断时,有可能刷新缓存,重新读取内存中的数据。在线程运行的过程中,如果某变量被其他线程修改,可能造成数据不一致的情况,从而导致结果错误。而 volatile 修饰的变量是线程可见的,当 JVM 解释 volatile 修饰的变量时,会通知 CPU,在计算过程中, 每次使用变量参与计算时,都会检查内存中的数据是否发生变化,而不是一直使用 CPU 缓存中的数据,可以保证计算结果的正确。
    volatile 只是通知底层计算时,CPU 检查内存数据,而不是让一个变量在多个线程中同步。

    volatile int count = 0;

    wait&notify

    AtomicXxx 类型组

    原子类型。
    在 concurrent.atomic 包中定义了若干原子类型,这些类型中的每个方法都是保证了原子操作的。多线程并发访问原子类型对象中的方法,不会出现数据错误。在多线程开发中,如果某数据需要多个线程同时操作,且要求计算原子性,可以考虑使用原子类型对象。

        AtomicInteger count = new AtomicInteger(0);
        void m(){
            count.incrementAndGet();
        }

    注意:原子类型中的方法是保证了原子操作,但多个方法之间是没有原子性的。如:

    AtomicInteger i = new AtomicInteger(0); 
    if(i.get() != 5){
        i.incrementAndGet();
    }

    在上述代码中,get 方法和 incrementAndGet 方法都是原子操作,但复合使用时,无法保证原子性,仍旧可能出现数据错误。

    CountDownLatch 门闩

    门闩是 concurrent 包中定义的一个类型,是用于多线程通讯的一个辅助类型。
    门闩相当于在一个门上加多个锁,当线程调用 await 方法时,会检查门闩数量,如果门

    闩数量大于 0,线程会阻塞等待。当线程调用 countDown 时,会递减门闩的数量,当门闩数量为 0 时,await 阻塞线程可执行。

    CountDownLatch latch = new CountDownLatch(5);
    
        void m1(){
            try {
                latch.await();// 等待门闩开放。
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("m1() method");
        }
    
        void m2(){
            for(int i = 0; i < 10; i++){
                if(latch.getCount() != 0){
                    System.out.println("latch count : " + latch.getCount());
                    latch.countDown(); // 减门闩上的锁。
                }
                try {
                    TimeUnit.MILLISECONDS.sleep(500);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                System.out.println("m2() method : " + i);
            }

    锁的重入

    在 Java 中,同步锁是可以重入的。只有同一线程调用同步方法或执行同步代码块,对同一个对象加锁时才可重入。
    当线程持有锁时,会在 monitor 的计数器中执行递增计算,若当前线程调用其他同步代码,且同步代码的锁对象相同时,monitor 中的计数器继续递增。每个同步代码执行结束,
    monitor 中的计数器都会递减,直至所有同步代码执行结束,monitor 中的计数器为 0 时,释放锁标记,_Owner 标记赋值为 null。

    ReentrantLock

    重入锁,建议应用的同步方式。相对效率比 synchronized 高。量级较轻。
    synchronized 在 JDK1.5 版本开始,尝试优化。到 JDK1.7 版本后,优化效率已经非常好了。在绝对效率上,不比 reentrantLock 差多少。
    使用重入锁,必须必须必须手工释放锁标记。一般都是在 finally 代码块中定义释放锁标记的 unlock 方法。

    公平锁

    这里写图片描述

    private static ReentrantLock lock = new ReentrantLock(true);
        public void run(){
            for(int i = 0; i < 5; i++){
                lock.lock();
                try{
                    System.out.println(Thread.currentThread().getName() + " get lock");
                }finally{
                    lock.unlock();
                }
            }
        }

    8ThreadLocal

    remove 问题
    这里写图片描述

    同步容器

    解决并发情况下的容器线程安全问题的。给多线程环境准备一个线程安全的容器对象。线程安全的容器对象: Vector, Hashtable。线程安全容器对象,都是使用 synchronized
    方法实现的。
    concurrent 包中的同步容器,大多数是使用系统底层技术实现的线程安全。类似 native。
    Java8 中使用 CAS。

    Map/Set

    ConcurrentHashMap/ConcurrentHashSet

    底层哈希实现的同步 Map(Set)。效率高,线程安全。使用系统底层技术实现线程安全。量级较 synchronized 低。key 和 value 不能为 null。

    ConcurrentSkipListMap/ConcurrentSkipListSet

    底层跳表(SkipList)实现的同步 Map(Set)。有序,效率比 ConcurrentHashMap 稍低。

    这里写图片描述

    List

    CopyOnWriteArrayList

    写时复制集合。写入效率低,读取效率高。每次写入数据,都会创建一个新的底层数组。

    Queue

    ConcurrentLinkedQueue

    基础链表同步队列。

    LinkedBlockingQueue
    阻塞队列,队列容量不足自动阻塞,队列容量为 0 自动阻塞。

    ArrayBlockingQueue
    底层数组实现的有界队列。自动阻塞。根据调用 API(add/put/offer)不同,有不同特性。
    当容量不足的时候,有阻塞能力。
    add 方法在容量不足的时候,抛出异常。
    put 方法在容量不足的时候,阻塞等待。
    offer 方法,
    单参数 offer 方法,不阻塞。容量不足的时候,返回 false。当前新增数据操作放弃。三参数 offer 方法(offer(value,times,timeunit)),容量不足的时候,阻塞 times 时长(单
    位为 timeunit),如果在阻塞时长内,有容量空闲,新增数据返回 true。如果阻塞时长范围
    内,无容量空闲,放弃新增数据,返回 false。

    DelayQueue
    延时队列。根据比较机制,实现自定义处理顺序的队列。常用于定时任务。如:定时关机。

    LinkedTransferQueue
    转移队列,使用 transfer 方法,实现数据的即时处理。没有消费者,就阻塞。

    SynchronusQueue
    同步队列,是一个容量为 0 的队列。是一个特殊的 TransferQueue。必须现有消费线程等待,才能使用的队列。
    add 方法,无阻塞。若没有消费线程阻塞等待数据,则抛出异常。
    put 方法,有阻塞。若没有消费线程阻塞等待数据,则阻塞。

    ThreadPool&Executor

    Executor

    线程池顶级接口。
    常用方法 - void execute(Runnable)
    作用是: 启动线程任务的。

    ExecutorService

    Executor 接口的子接口。
    常见方法 - Future submit(Callable), Future submit(Runnable)

    Future

    未来结果,代表线程任务执行结束后的结果。

    Callable

    可执行接口。
    接口方法 : Object call();相当于 Runnable 接口中的 run 方法。区别为此方法有返回值。不能抛出已检查异常。
    和 Runnable 接口的选择 - 需要返回值或需要抛出异常时,使用 Callable,其他情况可任意选择。

    Executors

    工具类型。为 Executor 线程池提供工具方法。类似 Arrays,Collections 等工具类型的功用。

    FixedThreadPool

    容量固定的线程池
    queued tasks - 任务队列
    completed tasks - 结束任务队列

    CachedThreadPool

    缓存的线程池。容量不限(Integer.MAX_VALUE)。自动扩容。默认线程空闲 60 秒,自动销毁。

    ScheduledThreadPool

    计划任务线程池。可以根据计划自动执行任务的线程池。

    SingleThreadExceutor

    单一容量的线程池。

    ForkJoinPool

    分支合并线程池(mapduce 类似的设计思想)。适合用于处理复杂任务。初始化线程容量与 CPU 核心数相关。
    线程池中运行的内容必须是 ForkJoinTask 的子类型(RecursiveTask,RecursiveAction)。

    WorkStealingPool

    JDK1.8 新增的线程池。工作窃取线程池。当线程池中有空闲连接时,自动到等待队列中窃取未完成任务,自动执行。
    初始化线程容量与 CPU 核心数相关。此线程池中维护的是精灵线程。
    ExecutorService.newWorkStealingPool();

    ThreadPoolExecutor

    线程池底层实现。除 ForkJoinPool 外,其他常用线程池底层都是使用 ThreadPoolExecutor
    实现的。
    public ThreadPoolExecutor
    (int corePoolSize, // 核心容量

    int maximumPoolSize, // 最大容量
    long keepAliveTime, // 生命周期,0 为永久
    TimeUnit unit, // 生命周期单位
    BlockingQueue workQueue // 任务队列,阻塞队列。
    );

    展开全文
  • 优秀的高并发编程视频,有源码案例,包含大规模网站的代码结构。
  • 19本高并发编程书籍推荐

    千次阅读 2020-05-13 08:49:42
    Java并发编程实战 (java并发的圣经) 多处理器编程的艺术 (并发编程的各种算法,java实现,有点难度) 并发的艺术 (多核处理器的共享内存模型中的各种算法) Java虚拟机并发编程 (jvm平台上...
  • Java高并发编程,构建并发编程知识体系,提升面试成功率-附件资源
  • (百度网盘)Java 高并发编程实战视频 完整版 (第一阶段、第二阶段、第三阶段)
  • java高并发源码 java-concurrency 参考Java高并发编程详解-多线程与架构设计-汪文君,学习笔记及源码
  • 下半年的跳槽季已经开始,好多同学已经拿到了不错的 Offer,同时还有一些同学对于 Java 高并发编程还缺少一些深入的理解,不过不用慌,今天老师分享的这份 27W 字的阿里巴巴 Java 高并发编程帮你保驾护航!...
  • 在诸多应用、诸多场景下被广泛使用,多线程技术作为 Java 语言和应用的基础能力,对其的学习、理解和掌握,不仅仅能够提升我们的技能,更能作为我们更好地理解面向对象编程、并发编程性能编程、分布式编程的入口...
  • │ 高并发编程第一阶段01讲、课程大纲及主要内容介绍.wmv │ 高并发编程第一阶段02讲、简单介绍什么是线程.wmv │ 高并发编程第一阶段03讲、创建并启动线程.mp4 │ 高并发编程第一阶段04讲、线程生命周期以及...
  • 但是,如果其中某一个任务也许会发生阻塞的话,那么即使是单处理器,使用并发编程也会带来很大的好处,这样,某个任务阻塞的时候,其他任务也可以继续运行了。 反应灵敏的用户界面:在单处理器上面性能提升最典型的...
  • 锁定某对象o,如果o的属性发生改变,不影响锁的使用,但是如果o变成另外一个对象,则锁定的对象发生改变,应该避免将锁定对象的引用变成另外一个对象。 public class Demo17 { Object o = new Object();...
  • 高并发,多线程编程实战讲解PDF文档,并包含demo源码。
  • JUC并发编程超详细详解篇

    万次阅读 多人点赞 2021-12-15 10:21:33
    文章目录前言一、JUC并发编程简介及准备1.1.本系列讲解思路1.2.知识储备1.3.用到的依赖1.4.logback.xml 配置二、进程与线程2.1.进程与线程2.1.1.进程2.1.2.线程2.1.3.对比2.2.并行与并发2.2.1.两者区别直观解释2.3....
  • JavaWeb 并发编程高并发解决方案

    万次阅读 多人点赞 2018-09-12 03:41:00
    在这里写写我学习到和自己所理解的 Java高并发编程和高并发解决方案。现在在各大互联网公司中,随着日益增长的互联网服务需求,高并发处理已经是一个非常常见的问题,在这篇文章里面我们重点讨论两个方面的问题,一...
  • 汪文君高并发编程实战视频 包括第一阶段、第二阶段、第三阶段的视频,真真的完整版
  • 下面就来介绍一下并发编程中的最为中要的三个特点。 并发编程三大特性 原子性   所谓的原子性在之前的博客中或者是在网上其他资料上都有所提及到。是指在一次的操作或者多次的操作过程中,要么所有的需要的操作...
  • 面试题(三):高并发编程

    千次阅读 2018-06-04 09:03:32
    高并发编程在 Java 5.0 提供了 java.util.concurrent(简称 JUC )包,在此包中增加了在并发编程中很常用的实用工具类,用于定义类似于线程的自定义子系统,包括线程池、异步 IO 和轻量级任务框架。01. 多线程和单...
  • 并发编程究竟学什么?
  • 学习并发编程,没有想象的那么难!!
  • 高并发编程实战1,2,3阶段视屏,
  • java高级并发编程32例

    2019-01-22 22:06:38
    深度解析java并发机制,Java高级开发,架构师思维导图 Java架构师必备技能,全方位仔细梳理所需各个知识点。让您有更多时间陪家人陪朋友
  • 高并发编程 汪文君高并发编程实战视频 第一阶段视频 百度网盘
  • 超推荐提升java高并发下的开发能力!!!

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 336,467
精华内容 134,586
关键字:

高并发编程

友情链接: HAWYQ.zip