前言
招聘黄金季,大厂每月都要吸纳几十名 Java 开发,对普通人来说,那可是几十张通往财务自由的船票。经验要求一般 3 年起,但很多 3 年经验的 Java,却在一面就挂得很惨。Java 3 年的“坎”,究竟是什么呢?耐心看完,一定对你有帮助。
技术岗,不是靠工作年限吃饭的岗位。3年经验是个幌子,我整理过一份详细的大厂岗位需求表,很多 20K 以上的 Java 岗,基本都要求具备高并发分布式的相关经验。老练的面试官知道,对于一个 Java 程序员而言,如果对并发编程有全面而深入的了解,那说明技术功底足够扎实。
所以,并发编程也是大厂面试的必考项。例如悲观锁和乐观锁分别适合在什么场景应用?线程、进程和协程的本质区别是什么?常见的高并发分布式系统架构有哪些?互联网流量激增的时代,对应用程序的并发性能、处理能力、处理时效性有着更高要求,高并发编程,直接成为资深开发和小白开发本质的分水岭。
我为大家整理了一下并发编程学习的11个点
- 并发编程的挑战
- Java并发机制的底层实现原理
- Java内存模型
- Java并发编程基础
- Java中的锁
- Java并发容器和框架
- Java中的13个原子操作类
- Java中的并发工具类
- Java中的线程池
- Executor框架
- Java并发编程实践
一 并发编程的挑战
并发编程的目的是为了让程序运行得更快,但是,并不是启动更多的线程就能让程序最大限度地并发执行。在进行并发编程时,如果希望通过多线程执行任务让程序运行得更快,会面临非常多的挑战,比如上下文切换的问题、死锁的问题,以及受限于硬件和软件的资源限制问题,本章会介绍几种并发编程的挑战以及解决方案。
1.1 上下文切换
1.2 死锁
1.3 资源限制的挑战
1.4 本章小结
二Java并发机制的底层实现原理
Java代码在编译后会变成Java字节码,字节码被类加载器加载到JVM里,JVM执行字节码,最终需要转化为汇编指令在CPU上执行,Java中所使用的并发机制依赖于JVM的实现和CPU的指令。本章我们将深入底层一起探索下Java并发机制的底层实现原理。
2.1 volatile的应用
2.2 synchronized的实现原理与应用
2.3 原子操作的实现原理
2.4 本章小结
三Java内存模型
Java线程之间的通信对程序员完全透明,内存可见性问题很容易困扰Java程序员,本章将揭开Java内存模型神秘的面纱。本章大致分4部分:Java内存模型的基础,主要介绍内存模型相关的基本概念;Java内存模型中的顺序一致性,主要介绍重排序与顺序一致性内存模型;同步原语,主要介绍3个同步原语(synchronized、volatile和final)的内存语义及重排序规则在处理器中的实现;Java内存模型的设计,主要介绍Java内存模型的设计原理,及其与处理器内存模型和顺序一致性内存模型的关系。
3.1 Java内存模型的基础
3.2 重排序
3.3 顺序一致性
3.4 volatile的内存语义
3.5 锁的内存语义
3.6 final域的内存语义
3.7 happens-before
3.8 双重检查锁定与延迟初始化
3.9 Java内存模型综述
3.10 本章小结
四线程简介
现代操作系统在运行一个程序时,会为其创建一个进程。例如,启动一个Java程序,操作系统就会创建一个Java进程。现代操作系统调度的最小单元是线程,也叫轻量级进程(LightWeight Process),在一个进程里可以创建多个线程,这些线程都拥有各自的计数器、堆栈和局部变量等属性,并且能够访问共享的内存变量。处理器在这些线程上高速切换,让使用者感觉到这些线程在同时执行。
4.1线程简介
4.2启动和终止线程
4.3线程间通信
4.4线程应用实例
4.5本章小结
五Java中的锁
本章将介绍Java并发包中与锁相关的API和组件,以及这些API和组件的使用方式和实现细节。内容主要围绕两个方面:使用,通过示例演示这些组件的使用方法以及详细介绍与锁相关的API;实现,通过分析源码来剖析实现细节,因为理解实现的细节方能更加得心应手且正确地使用这些组件。希望通过以上两个方面的讲解使开发者对锁的使用和实现两个层面有一定的了解。
5.1 Lock接口
5.2队列同步器
5.3重入锁
5.4读写锁
5.5 LockSupport工具
5.6 Condition接口
5.7本章小结
六Java并发容器和框架
Java程序员进行并发编程时,相比于其他语言的程序员而言要倍感幸福,因为并发编程大师Doug Lea不遗余力地为Java开发者提供了非常多的并发容器和框架。本章让我们一起来见识一下大师操刀编写的并发容器和框架,并通过每节的原理分析一起来学习如何设计出精妙的并发程序。
6.1 ConcurrentHashMap的实现原理与使用
6.2 ConcurrentLinkedQueue
6.3 Java中的阻塞队列
6.4 Fork/Join框架
6.5本章小结
七Java中的13个原子操作类
当程序更新一个变量时,如果多线程同时更新这个变量,可能得到期望之外的值,比如变量i=1,A线程更新i+1,B线程也更新i+1,经过两个线程操作之后可能i不等于3,而是等于2。因为A和B线程在更新变量i的时候拿到的i都是1,这就是线程不安全的更新操作,通常我们会使用synchronized来解决这个问题,synchronized会保证多线程不会同时更新变量i。而Java从JDK 1.5开始提供了java.util.concurrent.atomic包(以下简称Atomic包),这个包中的原子操作类提供了一种用法简单、性能高效、线程安全地更新一个变量的方式。因为变量的类型有很多种,所以在Atomic包里一共提供了13个类,属于4种类型的原子更新方式,分别是原子更新基本类型、原子更新数组、原子更新引用和原子更新属性(字段)。Atomic包里的类基本都是使用Unsafe实现的包装类。
7.1原子更新基本类型类
7.2原子更新数组
7.3原子更新引用类型
7.4原子更新字段类
7.5本章小结
八Java中的并发工具类
在JDK的并发包里提供了几个非常有用的并发工具类。CountDownLatch、CyclicBarrier和Semaphore工具类提供了一种并发流程控制的手段,Exchanger工具类则提供了在线程间交换数据的一种手段。本章会配合一些应用场景来介绍如何使用这些工具类。
8.1等待多线程完成的CountDownl ,atch
8.2同步屏障CyclicBarrier
8.3控制并发线程数的Semaphore
8.4线程间交换数据的Exchanger
8.5本章小结
九Java中的线程池
Java中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序都可以使用线程池。在开发过程中,合理地使用线程池能够带来3个好处。
第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
第三:提高线程的可管理性。线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,
还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。但是,要做到合理利用线程池,必须对其实现原理了如指掌。
9.1线程池的实现原理
9.2线程池的使用
9.3本章小结
十Executor框架
在Java中,使用线程来异步执行任务。Java线程的创建与销毁需要一定的开销,如果我们为每一个任务创建一个新线程来执行,这些线程的创建与销毁将消耗大量的计算资源。同时,为每一个任务创建一个新线程来执行,这种策略可能会使处于高负荷状态的应用最终崩溃。Java的线程既是工作单元,也是执行机制。从JDK 5开始,把工作单元与执行机制分离开来。工作单元包括Runnable和Callable,而执行机制由Executor框架提供。
10.1 Executor框架简介
10.2 ThreadPoolExecutor详解
10.3 ScheduledThreadPoolExecutor详解
10.4 FutureTask详解
10.5本章小结
十一Java并发编程实践
当你在进行并发编程时,看着程序的执行速度在自己的优化下运行得越来越快,你会觉得越来越有成就感,这就是并发编程的魅力。但与此同时,并发编程产生的问题和风险可能也会随之而来。本章先介绍几个并发编程的实战案例,然后再介绍如何排查并发编程造成的问题。
11.1生产者和消费者模式
11.2线上问题定位
11.3性能测试
11.4异步任务池
11.5本章小结
总结
具备了高并发编程能力,也就实现了从初级到资深的困难跨越;掌握了超高并发应用实战,你在工作中、团队里、面试时,都拥有了他人难以复制的核心竞争力,挑战20k、30k甚至40k的 Java 岗位也不在话下。
以上资料技术小编已经整理成400多页的文档,另外还有2020面试题,这份面试题的包含的模块分为19个模块,分别是: Java 基础、容器、多线程、反射、对象拷贝、Java Web 、异常、网络、设计模式、Spring/Spring MVC、Spring Boot/Spring Cloud、Hibernate、MyBatis、RabbitMQ、Kafka、Zookeeper、MySQL、Redis、JVM 。
关注公众号:程序员白楠楠,获取上述资料。