java并发编程整理

前言:本篇文章主要讲解java并发编程的知识。

目录

一、线程有关的基本概念

1.1线程安全

1.1.1线程安全的基本概念

1.1.2线程安全等级

1.2线程的同步异步、阻塞非阻塞

1.2.1区分同步异步、阻塞非阻塞

1.2.2同步异步使用场景

1.2.3分清同步异步的优势劣势

1.2.4阻塞

1.2.5非阻塞

1.3线程的并发和并行

1.4线程状态及java中线程常见方法

1.4.1线程的状态

1.4.2状态切换

1.4.3常见Thread方法用途和示例

1.5线程死锁和避免

1.5.1线程死锁的发生原因

1.5.2发现线程死锁的方法

1.5.3如何避免代码出现线程死锁

二、java内存模型及线程案例分析

2.1java内存模型

2.1.1cpu的内存模型

2.1.2java的内存模型

2.1.3熟悉指令重排和Happens-before规则

2.2synchronized和volatile关键字

2.3创建线程的几种方式

2.4ThreadLocal的定义和使用

2.5ThreadLocal的实现原理

三、线程池原理及应用

3.1线程池的创建和常用参数分析

3.1.1创建线程池的参数解义

3.1.2线程池状态分析

3.2常用线程池

3.2.1Java SDk的常用线程池

3.2.2线程池执行过程分析

3.3线程池常用列队值LinkedBlockingQueue

3.3.1LinkedBolockingQueue的数据结构

3.3.2LinkedBolockingQueue的实现原理

3.4可定时执行的线程池原理分析

3.4.1ScheduledExecutorService的使用场景

3.4.2DelayedWorkQueue的数据结构和实现原理

3.4.3ScheduledExcutorService的实现原理

3.5线程池的同步异步调用Callable,Future

四、java锁及应用

4.1乐观锁CAS实现及应用

4.1.1乐观锁悲观锁的区别

4.1.2java提供的乐观锁实现

4.2数据库悲观锁乐观锁实现

 4.2.1数据库悲观锁乐观锁的实现和区别

4.2.2数据库悲观锁乐观锁的应用场景

4.3AQS的数据结构

4.3.1AQS是什么

4.3.2AQS的数据结构

4.4ReentrantLoack的加锁解锁

4.4.1ReentrantLock简单加锁解锁过程

4.4.2ReentranLocak怎么实现可重入

4.4.3AQS加锁排列等待的实现

4.4.4公平锁和非公平锁的实现区别

五、并发容器及原理分析

5.1kv集合HashMap的实现原理

5.2HashMap在高并发场景下死循环分析

5.3ConcurrentHashMap如何解决高并发下的问题

5.4CopyOnWriteArrayList如何做到线程安全

 

一、线程有关的基本概念

1.1线程安全

1.1.1线程安全的基本概念

线程安全:一个类被多个线程以任意方式同时调用,且不需要外部额外同步和协同的情况下,仍然保持内部数据正确且表现正确的行为,那么这个类就是线程安全的。

1.1.2线程安全等级

线程安全等级:不可变、线程安全、有条件的线程安全、线程兼容、线程对立

a、不可变的对象一定是线程安全的

举例:final修饰的不可变类,如String、Integer,enum枚举类

b、线程安全 

线程安全类的任意方法操作都不会使该对象的数据处于不一致或者数据污染的情况

例如 ConcurrentHashMap、LinkedBlockingQueue

c、有条件的线程安全

对于单独的访问类的方法,是线程安全但是对于某些复合操作,需要外部类来同步

举例 Vector中的contains和add同时使用时是不安全的,组合使用时使用synchronized修饰

d、线程兼容类

使用synchronized控制同步访问每一个代码块或者类

举例 Collections.synchronizedList 来包装一个List,使其变为线程安全

e、线程对立类

不管是否调用了外部同步都不能在并发使用时保证其安全的类

举例  多线程买票

1.2线程的同步异步、阻塞非阻塞

1.2.1区分同步异步、阻塞非阻塞

同步的使用场景

a.大多数非异步场景(不用异步,就用同步来调用)

如百度搜索,客户端同步调用服务器搜索接口,等待服务器实时结构

b.在编排的流程中,必须等待拿到响应结果才能去做下一步操作,且在实时链路中相互之间有数据串联或关联数据的

如电商中商品详情页的查询接口的内部实现

异步

非阻塞式调用,立即返回,调用方无需等待响应方返回实际结果,响应方会通过状态、通知或回调告知调用方

1.2.2同步异步使用场景

1.耗时任务。主线程中提交耗时任务到线程池,然后通过Future来异步获取任务执行结果,这里也可以由异步任务发消息等途径来通知主线程

2.业务链路太长,可以把非核心的部分用异步实现

1.2.3分清同步异步的优势劣势

同步优点:

1.可以拿到实时结果进行处理,上下问信息始终在一个代码块,代码处理上更加方便直观

2.对错误和异常处理可以做到实时

同步缺点:

1.耗时的接口响应会影响整个流程的性能

异步优点:

1.不影响主线程的执行,降低响应时间,提高应用的性能和效率

2.及时释放系统资源,如线程占用,让系统去做更有价值的事

异步缺点:

1.为了保障数据最终一致性,需要对账系统去做好监控和风险

2.需要更多异步任务去补偿系统间的数据一致性

1.2.4阻塞

调用结果返回之前,当前线程会被挂起,调用线程只有在得到结果之后才会返回

1.2.5非阻塞

非阻塞调用指在不能立刻得到结果之前,该调用不会则赛当前线程,而会立即返回。

1.3线程的并发和并行

并发指宏观上,逻辑上单个cpu轮流处理

并行指物理上,多个cpu在同时处理

1.4线程状态及java中线程常见方法

1.4.1线程的状态

在java.lang.Thread中的内部枚举State中可见

1.4.2状态切换

1.4.3常见Thread方法用途和示例

Thread.yield()

线程让步,使用了这个方法,当前线程就会推出cpu时间片,让其他线程或当前线程使用cpu时间片执行

Thread.sleep()

线程休眠,主动让出当前cpu时间,在指定时间过后,cpu会返回继续执行该线程,sleep方法不会释放当前所持有的锁

Thread.join()

等待该线程死亡/终止,当前线程会等待调用该方法的线程执行完毕后才能继续执行

Object.wait()和Object.notifyAll()

wait()方法调用前必须拥有对象锁,例如在synchronized代码块内,调用wait方法后,对象锁会释放,

线程进入WAITTING等待状态

1.5线程死锁和避免

1.5.1线程死锁的发生原因

1.5.2发现线程死锁的方法

jstack [java进程pid] 找出死锁地方

1.5.3如何避免代码出现线程死锁

避免获取锁顺序不一致

二、java内存模型及线程案例分析

2.1java内存模型

2.1.1cpu的内存模型

L1和L2是每个cpu自己的高速缓存

L3是多cpu之间共享的缓存

memory是主存

2.1.2java的内存模型

每个线程都有自己的工作内存

工作内存包含线程本地局部变量和主内存的副本拷贝

线程之间的共享变量通过主内存在各线程间同步

2.1.3熟悉指令重排和Happens-before规则

java源代码会经过编译重排,处理器重排等过程,Happens-before是保证代码能先后执行的一种规则

2.2synchronized和volatile关键字

synchronized可以修饰方法和代码块

修饰方法普通方法时锁对象是类实例出来的对象

修饰静态方法时锁对象是类本身

ps:javap是反编译工具

volatile修饰的属性是强调可见性

2.3创建线程的几种方式

a.new Thread(runnable).start()

b.new MyThread().start()

c.new Thread(new FutureTask(callable)).start()

d.Excutors框架

2.4ThreadLocal的定义和使用

new ThreadLocal<>(),注意remove,不然有oom(outofmemory)

2.5ThreadLocal的实现原理

实现原理为ThreadLocalMap,其中entry中的key就是Thread ,value就是我们储存的数据对象

三、线程池原理及应用

3.1线程池的创建和常用参数分析

3.1.1创建线程池的参数解义

下面是ThreadPoolExecutor的构造方法参数

a.corePoolSize 核心线程数,保持在线程池中线程的数量

b.maximumPoolSize 线程池允许的最大线程数

c.keepAliveTime/timeUnit 线程池中线程空闲不被释放的最大时间,配合timeUnit使用,为0表示永远不被释放

d.workQueue BlockingQueue<Runnable>,工作线程任务的阻塞列队,用来存放等待执行的任务,默认实现:LinkedBlockingQueue

e.threadFactory 线程池创建工厂,子类通过自定义实现接口Thread newThread(Runnable) 通过工厂创建线程池具体的Thread线程  默认实现: DefaultThreadFactory

f.handler(RejectedExecutionHandler)

当workQueue无法存放新加任务,或添加人物后线程池停止工作,使用设置的拒绝策略拒绝新加任务的执行,可以用rejectedExecution来实现自己的拒绝策略

默认拒绝策略;AbortPolicy,直接抛出异常

CallerRunsPolicy:调用方执行策略,当前调用线程或添加任务的线程执行

DiscardPolicy:直接抛弃策略,对任务不做任何事情,忽略该任务

DiscardOldestPolicy 抛弃最早任务策略,将workQueue的一个任务取出抛弃,经当前任务放入workQueue中执行

3.1.2线程池状态分析

在ThreadPoolExecutor类中有相应的列举

 

3.2常用线程池

3.2.1Java SDk的常用线程池

a.固定线程数量的线程池

Executors.newFixedThreadPool()

b.单线程的线程池

Executors.newSingleThreadPool()

c.可缓存的线程池

Executors.newCachedThreadPool()

.这里使用同步列队

d.定时执行的线程池

Executors.newScheduledThreadPool()

使用了DelayedWorkQueue延时列队,通过延时队列来控制时间来执行

3.2.2线程池执行过程分析

看源码,入口为ThreadPoolExcutor中的execute(Runnable command)

3.3线程池常用列队值LinkedBlockingQueue

3.3.1LinkedBolockingQueue的数据结构

3.3.2LinkedBolockingQueue的实现原理

添加元素

取出元素

3.4可定时执行的线程池原理分析

3.4.1ScheduledExecutorService的使用场景

1.定时执行异步任务

2.周期执行异步任务

3.4.2DelayedWorkQueue的数据结构和实现原理

最小堆结构,以距离当前时间的值作为排序依据

3.4.3ScheduledExcutorService的实现原理

主要依据的是DelayedWorkQueue这个特殊的列队实现的,spring中@Scheduled注解本质上就是ScheduledExcutorService

3.5线程池的同步异步调用Callable,Future

future是通过执行callable中的call方法,而call方法可能是一个耗时方法,把call执行的返回值通过共享变量的方式(FutureTask)在执行任务的线程与获取Future的主线程中通信

四、java锁及应用

4.1乐观锁CAS实现及应用

4.1.1乐观锁悲观锁的区别

为什么加锁: 为了保证多个线程更新一个资源时,防止数据冲突和脏乱,做到线程安全

CAS解释

全名:compare and swap,先比较然后设置,CAS是一个原子操作(基于操作系统,JNI的方式实现)

适用场景

更新一个值,不依赖于加锁实现,可以接收CAS失败

局限

只可以更新一个值,如AtomicReference、AtomicInteger需要同时更新时,无法做到原子性

4.1.2java提供的乐观锁实现

cas可能会出现ABA的问题

4.2数据库悲观锁乐观锁实现

 4.2.1数据库悲观锁乐观锁的实现和区别

select ...lock in share mode 共享锁

select ...for update 排他锁

update set  ... version = vesion+1 where version = $version$

cas思路,使用version版本控制

4.2.2数据库悲观锁乐观锁的应用场景

悲观锁的应用场景

并发不是很高,或者要求效率很高的情况下

4.3AQS的数据结构

4.3.1AQS是什么

AbstractQueuedSynchronizer

提供一个框架来实现阻塞所和相关的依赖于先进先出等待队列

各种同步组件的核心抽象实现类

管理等待队列,锁的占用和释放,中断、超时和通知等

4.3.2AQS的数据结构

具体看AQS类的源码即可

4.4ReentrantLoack的加锁解锁

4.4.1ReentrantLock简单加锁解锁过程

4.4.2ReentranLocak怎么实现可重入

4.4.3AQS加锁排列等待的实现

获取锁的时候

释放锁的时候

4.4.4公平锁和非公平锁的实现区别

非公平锁lock的时候,及时有等待的列队,他也会cas操作尝试获取锁

公平锁的实现在获取锁的时候先判断双向列队是否有在等待的线程

五、并发容器及原理分析

5.1kv集合HashMap的实现原理

各jdk版本不太相同,大致都是数组加链表的结构,jdk1.7是数据加单向链表 jdk1.8是数组加红黑树结构,后来的加在最后面

5.2HashMap在高并发场景下死循环分析

jdk1.7出现死循环分析

5.3ConcurrentHashMap如何解决高并发下的问题

jdk1.7中ConcurrentHashMap的数据结构,注意Segment是继承了ReentrantLock的,整体上是分段锁的思想

jdk1.8中已经去掉分段锁了,直接使用synchronized关键字了。下图是jdk1.7的数据结构图

5.4CopyOnWriteArrayList如何做到线程安全

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值