java高并发解决方案_长文慎入-探索Java并发编程与高并发解决方案

所有示例代码,请见/下载于

dab72564a864171cb39f6d4fe675c213.png

58e35c030bd96df86de102950b24ab1a.png

17899830fd82db8410271ffd3f82b1ae.png

1 基本概念

1.1 并发

同时拥有两个或者多个线程,如果程序在单核处理器上运行多个线程将交替地换入或者换出内存,这些线程是同时“存在"的,每个线程都处于执行过程中的某个状态,如果运行在多核处理器上,此时,程序中的每个线程都将分配到一个处理器核上,因此可以同时运行.

1.2 高并发( High Concurrency)

互联网分布式系统架构设计中必须考虑的因素之一,通常是指,通过设计保证系统能够同时并行处理很多请求.

1.3 区别与联系

并发: 多个线程操作相同的资源,保证线程安全,合理使用资源

高并发:服务能同时处理很多请求,提高程序性能

2 CPU

2.1 CPU 多级缓存

a8c33016f3faacb03e0e0c53ad13b501.png

为什么需要CPU cache

CPU的频率太快了,快到主存跟不上

如此,在处理器时钟周期内,CPU常常需要等待主存,浪费资源。所以cache的出现,是为了缓解CPU和内存之间速度的不匹配问题(结构:cpu-> cache-> memory ).

CPU cache的意义

1) 时间局部性

如果某个数据被访问,那么在不久的将来它很可能被再次访问

2) 空间局部性

如果某个数据被访问,那么与它相邻的数据很快也可能被访问

2.2 缓存一致性(MESI)

用于保证多个 CPU cache 之间缓存共享数据的一致

M-modified被修改

该缓存行只被缓存在该 CPU 的缓存中,并且是被修改过的,与主存中数据是不一致的,需在未来某个时间点写回主存,该时间是允许在其他CPU 读取主存中相应的内存之前,当这里的值被写入主存之后,该缓存行状态变为 E

E-exclusive独享

缓存行只被缓存在该 CPU 的缓存中,未被修改过,与主存中数据一致

可在任何时刻当被其他 CPU读取该内存时变成 S 态,被修改时变为 M态

S-shared共享

该缓存行可被多个 CPU 缓存,与主存中数据一致

I-invalid无效

a723ed60ebc20c9652ed30cc3ad28068.png

乱序执行优化

处理器为提高运算速度而做出违背代码原有顺序的优化

并发的优势与风险

af56afc3e42a67e90b1d5fcaa171952c.png

3 项目准备

3.1 项目初始化

5d8b4bdc0e220b8a8fecc609bf008a3e.png

d15acd003b27078ee774070b92e9f178.png

4a558701531034ad41858c11c2ab5151.png

3.2 并发模拟-Jmeter压测

8c3de2ab517ed07571a8a322f5269317.png

6e9049f97f9ddb766fcb56cc789d110d.png

e0d7dfd58687eb3dab59103c733350bb.png

10ce8bdc407b91ff4f41422e0575d7f7.png

cb2593d8515e58c626be84750dd8e8b7.png

3.3 并发模拟-代码

CountDownLatch

67805e97ae9fe5ae0ff616865fd96258.png

Semaphore(信号量)

b58d9bd0b92d23f0ade19d217be85080.png

以上二者通常和线程池搭配

下面开始做并发模拟

package com.mmall.concurrency;

import com.mmall.concurrency.annoations.NotThreadSafe;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.CountDownLatch;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.Semaphore;

/**

* @author shishusheng

* @date 18/4/1

*/

@Slf4j

@NotThreadSafe

public class ConcurrencyTest {

/**

* 请求总数

*/

public static int clientTotal = 5000;

/**

* 同时并发执行的线程数

*/

public static int threadTotal = 200;

public static int count = 0;

public static void main(String[] args) throws Exception {

//定义线程池

ExecutorService executorService = Executors.newCachedThreadPool();

//定义信号量,给出允许并发的线程数目

final Semaphore semaphore = new Semaphore(threadTotal);

//统计计数结果

final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);

//将请求放入线程池

for (int i = 0; i < clientTotal ; i++) {

executorService.execute(() -> {

try {

//信号量的获取

semaphore.acquire();

add();

//释放

semaphore.release();

} catch (Exception e) {

log.error("exception", e);

}

countDownLatch.countDown();

});

}

countDownLatch.await();

//关闭线程池

executorService.shutdown();

log.info("count:{}", count);

}

/**

* 统计方法

*/

private static void add() {

count++;

}

}

运行发现结果随机,所以非线程安全

4线程安全性

4.1 线程安全性

当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些进程将如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的

4.2 原子性

4.2.1 Atomic 包

AtomicXXX:CAS,Unsafe.compareAndSwapInt

提供了互斥访问,同一时刻只能有一个线程来对它进行操作

package com.mmall.concurrency.example.atomic;

import com.mmall.concurrency.annoations.ThreadSafe;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.CountDownLatch;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.Semaphore;

import java.util.concurrent.atomic.AtomicLong;

/**

* @author shishusheng

*/

@Slf4j

@ThreadSafe

public class AtomicExample2 {

/**

* 请求总数

*/

public static int clientTotal = 5000;

/**

* 同时并发执行的线程数

*/

public static int threadTotal = 200;

/**

* 工作内存

*/

public static AtomicLong count = new AtomicLong(0);

public static void main(String[] args) throws Exception {

ExecutorService executorService = Executors.newCachedThreadPool();

final Semaphore semaphore = new Semaphore(threadTotal);

final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);

for (int i = 0; i < clientTotal ; i++) {

executorService.execute(() -> {

try {

System.out.println();

semaphore.acquire();

add();

semaphore.release();

} catch (Exception e) {

log.error("exception", e);

}

countDownLatch.countDown();

});

}

countDownLatch.await();

executorService.shutdown();

//主内存

log.info("count:{}", count.get());

}

private static void add() {

count.incrementAndGet();

// count.getAndIncrement();

}

}

package com.mmall.concurrency.example.atomic;

import com.mmall.concurrency.annoations.ThreadSafe;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.atomic.AtomicReference;

/**

* @author shishusheng

* @date 18/4/3

*/

@Slf4j

@ThreadSafe

public class AtomicExample4 {

private static AtomicReference count = new AtomicReference<>(0);

public static void main(String[] args) {

// 2

count.compareAndSet(0, 2);

// no

count.compareAndSet(0, 1);

// no

count.compareAndSet(1, 3);

// 4

count.compareAndSet(2, 4);

// no

count.compareAndSet(3, 5);

log.info("count:{}", count.get());

}

}

dda12652c4738c54287e7fd9cded14af.png

AtomicReference,AtomicReferenceFieldUpdater

494de9ad7a425016ae8028cdb23235f3.png

AtomicBoolean

049e4405e1d462f1864d9818d01de7c4.png

AtomicStampReference : CAS的 ABA 问题

4.2.2 锁

synchronized:依赖 JVM

修饰代码块:大括号括起来的代码,作用于调用的对象

修饰方法: 整个方法,作用于调用的对象

fe07350c402d8f9beb088a0604a5162a.png

修饰静态方法:整个静态方法,作用于所有对象

d0932fcb8aa2c2080ed6427558c791e4.png

package com.mmall.concurrency.example.count;

import com.mmall.concurrency.annoations.ThreadSafe;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.CountDownLatch;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.Semaphore;

/**

* @author shishusheng

*/

@Slf4j

@ThreadSafe

public class CountExample3 {

/**

* 请求总数

*/

public static int clientTotal = 5000;

/**

* 同时并发执行的线程数

*/

public static int threadTotal = 200;

public static int count = 0;

public static void main(String[] args) throws Exception {

ExecutorService executorService = Executors.newCachedThreadPool();

final Semaphore semaphore = new Semaphore(threadTotal);

final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);

for (int i = 0; i < clientTotal ; i++) {

executorService.execute(() -> {

try {

semaphore.acquire();

add();

semaphore.release();

} catch (Exception e) {

log.error("exception", e);

}

countDownLatch.countDown();

});

}

countDownLatch.await();

executorService.shutdown();

log.info("count:{}", count);

}

private synchronized static void add() {

count++;

}

}

synchronized 修正计数类方法

修饰类:括号括起来的部分,作用于所有对象

子类继承父类的被 synchronized 修饰方法时,是没有 synchronized 修饰的!!!

Lock: 依赖特殊的 CPU 指令,代码实现

4.2.3 对比

synchronized: 不可中断锁,适合竞争不激烈,可读性好

Lock: 可中断锁,多样化同步,竞争激烈时能维持常态

Atomic: 竞争激烈时能维持常态,比Lock性能好; 只能同步一

个值

4.3 可见性

一个线程对主内存的修改可以及时的被其他线程观察到

4.3.1 导致共享变量在线程间不可见的原因

线程交叉执行

重排序结合线程交叉执行

共享变量更新后的值没有在工作内存与主存间及时更新

4.3.2 可见性之synchronized

JMM关于synchronized的规定

线程解锁前,必须把共享变量的最新值刷新到主内存

线程加锁时,将清空工作内存中共享变量的值,从而使

用共享变量时需要从主内存中重新读取最新的值(加锁与解锁是同一把锁)

4.3.3 可见性之volatile

通过加入内存屏障和禁止重排序优化来实现

对volatile变量写操作时,会在写操作后加入一条store

屏障指令,将本地内存中的共享变量值刷新到主内存

对volatile变量读操作时,会在读操作前加入一条load

屏障指令,从主内存中读取共享变量

ee93db9bc12496d09e8424ed0cac5f6e.png

b9ff83617de27566f76c92af062d45e7.png

55104dd38f48b32cc118bd0c72d54518.png

volatile使用

volatile boolean inited = false;

//线程1:

context = loadContext();

inited= true;

// 线程2:

while( !inited ){

sleep();

}

doSomethingWithConfig(context)

4.4 有序性

一个线程观察其他线程中的指令执行顺序,由于指令重排序的存在,该观察结果一般杂乱无序

JMM允许编译器和处理器对指令进行重排序,但是重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性

4.4.1 happens-before 规则

5发布对象

bcd1ae46002737403cc9e0c1df7b2603.png

68739c23d900a517f4f0fce2c504d9cf.png

44192ec1c8e73e130aba44ac2431b87b.png

5.1 安全发布对象

9abc5cbb0059da7f70f612dc19760d20.png

5bb26367ab80298ec5c43a48c54610a5.png

4e6ab5bba35889472ca06a1bf6f6062f.png

687e867c8a620ddba6be3dccddf5fbf6.png

package com.mmall.concurrency.example.singleton;

import com.mmall.concurrency.annoations.NotThreadSafe;

/**

* 懒汉模式 -》 双重同步锁单例模式

* 单例实例在第一次使用时进行创建

* @author shishusheng

*/

@NotThreadSafe

public class SingletonExample4 {

/**

* 私有构造函数

*/

private SingletonExample4() {

}

// 1、memory = allocate() 分配对象的内存空间

// 2、ctorInstance() 初始化对象

// 3、instance = memory 设置instance指向刚分配的内存

// JVM和cpu优化,发生了指令重排

// 1、memory = allocate() 分配对象的内存空间

// 3、instance = memory 设置instance指向刚分配的内存

// 2、ctorInstance() 初始化对象

/**

* 单例对象

*/

private static SingletonExample4 instance = null;

/**

* 静态的工厂方法

*

* @return

*/

public static SingletonExample4 getInstance() {

// 双重检测机制 // B

if (instance == null) {

// 同步锁

synchronized (SingletonExample4.class) {

if (instance == null) {

// A - 3

instance = new SingletonExample4();

}

}

}

return instance;

}

}

3f78d448fcf204e1fa7c7686d4f458b5.png

300c8052646daa04e26503714cbfe08b.png

7 AQS

7.1 介绍

c689b04f86918fbd33243005d350fecc.png

使用Node实现FIFO队列,可以用于构建锁或者其他同步装置的基础框架

利用了一个int类型表示状态

使用方法是继承

子类通过继承并通过实现它的方法管理其状态{acquire 和release} 的方法操纵状态

可以同时实现排它锁和共享锁模式(独占、共享)

同步组件

CountDownLatch

package com.mmall.concurrency.example.aqs;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.CountDownLatch;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

/**

* @author shishusheng

*/

@Slf4j

public class CountDownLatchExample1 {

private final static int threadCount = 200;

public static void main(String[] args) throws Exception {

ExecutorService exec = Executors.newCachedThreadPool();

final CountDownLatch countDownLatch = new CountDownLatch(threadCount);

for (int i = 0; i < threadCount; i++) {

final int threadNum = i;

exec.execute(() -> {

try {

test(threadNum);

} catch (Exception e) {

log.error("exception", e);

} finally {

countDownLatch.countDown();

}

});

}

countDownLatch.await();

log.info("finish");

exec.shutdown();

}

private static void test(int threadNum) throws Exception {

Thread.sleep(100);

log.info("{}", threadNum);

Thread.sleep(100);

}

}

package com.mmall.concurrency.example.aqs;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.CountDownLatch;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.TimeUnit;

/**

* 指定时间内处理任务

*

* @author shishusheng

*

*/

@Slf4j

public class CountDownLatchExample2 {

private final static int threadCount = 200;

public static void main(String[] args) throws Exception {

ExecutorService exec = Executors.newCachedThreadPool();

final CountDownLatch countDownLatch = new CountDownLatch(threadCount);

for (int i = 0; i < threadCount; i++) {

final int threadNum = i;

exec.execute(() -> {

try {

test(threadNum);

} catch (Exception e) {

log.error("exception", e);

} finally {

countDownLatch.countDown();

}

});

}

countDownLatch.await(10, TimeUnit.MILLISECONDS);

log.info("finish");

exec.shutdown();

}

private static void test(int threadNum) throws Exception {

Thread.sleep(100);

log.info("{}", threadNum);

}

}

Semaphore用法

25dfd3ff5f96b5700c9834d572b74eea.png

6cd1ca66ce6705c4b7a2698d66f2b464.png

d6ec4d6f945ae085af252fc3e1a3fb4a.png

CycliBarrier

package com.mmall.concurrency.example.aqs;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.CyclicBarrier;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

/**

* @author shishusheng

*/

@Slf4j

public class CyclicBarrierExample1 {

private static CyclicBarrier barrier = new CyclicBarrier(5);

public static void main(String[] args) throws Exception {

ExecutorService executor = Executors.newCachedThreadPool();

for (int i = 0; i < 10; i++) {

final int threadNum = i;

Thread.sleep(1000);

executor.execute(() -> {

try {

race(threadNum);

} catch (Exception e) {

log.error("exception", e);

}

});

}

executor.shutdown();

}

private static void race(int threadNum) throws Exception {

Thread.sleep(1000);

log.info("{} is ready", threadNum);

barrier.await();

log.info("{} continue", threadNum);

}

}

6438eeaf9e86b40437658440771799d6.png

package com.mmall.concurrency.example.aqs;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.CyclicBarrier;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.TimeUnit;

/**

* @author shishusheng

*/

@Slf4j

public class CyclicBarrierExample2 {

private static CyclicBarrier barrier = new CyclicBarrier(5);

public static void main(String[] args) throws Exception {

ExecutorService executor = Executors.newCachedThreadPool();

for (int i = 0; i < 10; i++) {

final int threadNum = i;

Thread.sleep(1000);

executor.execute(() -> {

try {

race(threadNum);

} catch (Exception e) {

log.error("exception", e);

}

});

}

executor.shutdown();

}

private static void race(int threadNum) throws Exception {

Thread.sleep(1000);

log.info("{} is ready", threadNum);

try {

barrier.await(2000, TimeUnit.MILLISECONDS);

} catch (Exception e) {

log.warn("BarrierException", e);

}

log.info("{} continue", threadNum);

}

}

1d4fe2cfef14fdf39a429f6f498ee576.png

package com.mmall.concurrency.example.aqs;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.Semaphore;

/**

* @author shishusheng

*/

@Slf4j

public class SemaphoreExample3 {

private final static int threadCount = 20;

public static void main(String[] args) throws Exception {

ExecutorService exec = Executors.newCachedThreadPool();

final Semaphore semaphore = new Semaphore(3);

for (int i = 0; i < threadCount; i++) {

final int threadNum = i;

exec.execute(() -> {

try {

// 尝试获取一个许可

if (semaphore.tryAcquire()) {

test(threadNum);

// 释放一个许可

semaphore.release();

}

} catch (Exception e) {

log.error("exception", e);

}

});

}

exec.shutdown();

}

private static void test(int threadNum) throws Exception {

log.info("{}", threadNum);

Thread.sleep(1000);

}

}

9 线程池

9.1 newCachedThreadPool

5430d28b90ec29dddc7fccefe2fd7ce4.png

9.2 newFixedThreadPool

2f48e4fa9bed74f3cd5d768581dd91b9.png

9.3 newSingleThreadExecutor

看出是顺序执行的

ca085222b367ff6d5c0aa44faa77eee1.png

9.4 newScheduledThreadPool

e4cbf4f164875967ffcb9ee29fded25c.png

10f6f23a2d5ec7c0d118f6e4f92a0c39.png

10 死锁

be5fcf1b738db32e586cd79be4ea7207.png

b51d2f6c50b35d92d0cbc6985e9b9980.png

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值