1 并发与高并发基本概念
并发:同时拥有两个或者多个线程,如果程序在单核处理器上运行,多个线程将交替地换入或者换出内存,这些线程是同时"存在"的,每个线程都处于执行过程中的某个状态如果运行在多核处理器上,此时程序中每个线程都将分配到一个处理器核上,因此,可以同时运行
高并发:是互联网分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计保证系统能够同时并行处理很多请求.
1 CPU缓存
在计算机中,所有的运算操作都是由CPU寄存器来完成的,CPU指令的执行过程需要涉及数据的读取和写入操作,CPU所能访问的所有数据只能是计算机的主存(RAM).由于硬件上面的发展,导致CPU的处理速度和内存的访问速度之间差距越拉越大,由于两边速度严重的不对等,CPU的整体吞吐量降低,于是就有了在CPU和主内存之间增加缓存的设计,缓存的数量可以增加到3级了.CPU Cache又是由很多个Cache line构成的,Cache line是CPU Cache中的最小的缓存单位,目前主流的Cache line大小都是64字节,
Cache的出现是为了解决CPU直接访问内存效率低下问题的,程序在运行过程中,会将运算所需要的数据从主存复制一份到Cache中,这样CPU计算时就可以直接对CPU Cache中的数据进行读取和写入,当运算结束后,再将CPU Cache中的最新数据刷新到主内存中,通过这种方式大大提高了CPU的吞吐能力.
CPU缓存有什么意义
- 时间局部性:如果某个数据被访问,那么不久的将来它很可能被再次访问
- 空间局部性:如果某个数据被访问,那么与它相邻的数据很快也可能被访问
CPU多级缓存-乱序执行优化
处理器为提高运算速度而做出违背代码原有顺序的优化,单核情况无影响,在多核情况下数据结果可能不一致.
2 CPU缓存一致性
在多线程的情况下,例如i++的操作,多个线程执行共1000次操作,结果很有可能小于10000.解决办法之一是使用缓存一致性协议,最出名的协议为MESI协议,MESI协议保证了每一个缓存中使用的共享变量副本都是一致的,当CPU在操作Cache中的数据时,如果发现该变量是一个共享变量,也就是其他Cache中也存在一个副本,那么:1)读取操作,不过任何处理,知识将Cache中的数据读取到寄存器.2)写入操作,发出信号通知其他CPU将该变量的Cache line置为无效状态,其他CPU在进行该变量读入的时候补得不到主内存中再次获取.
3 PostMan
http://localhost:8080可通过{
{}}获取并且访问"/test"地址,这样可以统一环境.更加方便
4 JMeter使用
添加线程组
添加http请求
察看结果树监听
图形结果树监听
5 线程安全性
1.原子性:同一时刻只能有一个线程访问
2.有序性:禁止指令重排序,保证代码执行顺序
3.可见性:对主内存的修改,其他线程能立即知道.
6 Atomic包
AtomicInteger,AtomicLong等,底层是使用cas来保障数据的原子性
package juc.count;
import juc.annotations.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.AtomicInteger;
@Slf4j
@ThreadSafe
public class CountExample2 {
// 请求总数
private static final int clientTotal = 5000;
// 线程数
private static final int threadTotal = 50;
private static AtomicInteger count = new AtomicInteger(0);
private static void add(){
count.incrementAndGet();
}
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) {
e.printStackTrace();
}
countDownLatch.countDown();
});
}
countDownLatch.await();
log.info("count: {}", count);
executorService.shutdown();
}
}
JVM在底层处理long,double计算的时候,会将64位拆成两个32位计算,但是现在的JVM基本都会保证long,double计算的原子性.
LongAdder和DoubleAdder对Atomic进行高并发的优化,缺点是Adder在统计的时候如果有并发更新,可能导致统计的数据有误差。
package juc.count;
import juc.annotations.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.LongAdder;
@Slf4j
@ThreadSafe
public class CountExample4 {
// 请求总数
private static final int clientTotal = 5000;
// 线程数
private static final int threadTotal = 50;
private static LongAdder count = new LongAdder();
private static void add(){
count.increment();
}
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) {
e.printStackTrace();
}
countDownLatch.countDown();
});
}
countDownLatch.await();
log.info("count: {}", count);
executorService.shutdown();
}
}
AtomicReference:以原子读写的对象引用变量
package juc.count;
import juc.annotations.ThreadSafe;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.atomic.AtomicReference;
@Slf4j
@ThreadSafe
public class CountExample5 {
// 请求总数
private static final int clientTotal = 5000;
// 线程数
private static final int threadTotal = 50;
private static AtomicReference<Integer> count = new AtomicReference(0);
public static void main(String[] args) throws Exception {
count.compareAndSet(0,1);
count.compareAndSet(3,6);
count.compareAndSet(1,5);
count.compareAndSet(6,2);
count.compareAndSet(5,9);
log.info("success, {}", count);
}
}
AtomicIntegerFieldUpdater:原子性的更新对象的变量–必须volatile定义
package juc.count;
import juc.annotations.ThreadSafe;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
@Slf4j
@ThreadSafe
public class CountExample6 {
// 请求总数
private static final int clientTotal = 5000;
// 线程数
private static final int threadTotal = 50;
private static CountExample6 example6 = new CountExample6();
//原子性的更新对象的变量--必须volatile定义
private static AtomicIntegerFieldUpdater<CountExample6> update = AtomicIntegerFieldUpdater.newUpdater(CountExample6.class, "count");
@Getter
private volatile int count = 100;
public static void main(String[] args) throws Exception {
if(update.compareAndSet(example6 ,100, 200)){
log.info("success 1, {}", example6.getCount());
}
if(update.compareAndSet(example6 ,100, 300)){
log.info("success 2, {}", example6.getCount());
} else {
log.info("fail, {}", example6.getCount());
}
}
}
AtomicStamp