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());
}
}
}
AtomicStampedReference:解决cas的ABA问题
package juc.count;
import juc.annotations.ThreadSafe;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.atomic.AtomicStampedReference;
@Slf4j
@ThreadSafe
public class CountExample7 {
// 请求总数
private static final int clientTotal = 5000;
// 线程数
private static final int threadTotal = 50;
private static CountExample7 example6 = new CountExample7();
private static AtomicStampedReference update = new AtomicStampedReference(100, 200);
@Getter
private volatile int count = 100;
public static void main(String[] args) throws Exception {
log.info("success, {}", update.getStamp());
if(update.compareAndSet(100, 101, update.getStamp(), update.getStamp() + 1)){
log.info("success 1, {}", update.getReference());
}
}
}
6 synchronized
修饰代码块:大括号括起来的代码,作用于调用的对象
修饰方法:整个方法,作用于调用的对象
修饰静态方法:整个静态方法,作用于所有对象
修饰类:括号括起来的部分,作用于所有对象
package juc.count;
import juc.annotations.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;
@Slf4j
@ThreadSafe
public class CountExample1 {
// 请求总数
private static final int clientTotal = 5000;
// 线程数
private static final int threadTotal = 50;
private static int count = 0;
private static synchronized void add(){
count++;
}
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();
}
}
JMM关于synchronized的规定
线程解锁前,必须把共享变量的最新值刷新到主内存
线程加锁时,讲清空工作内存中共享变量的值,从而使用共享变量时需要从主内存中重新读取最新值
7 安全发布对象
发布对象:使一个对象能够被当前范围之外的代码所使用.
package juc.publish;
import juc.annotations.NotThreadSafe;
import lombok.extern.slf4j.Slf4j;
import java.util.Arrays;
@Slf4j
@NotThreadSafe
public class ExamplePublish1 {
private String[] states = {"a","b","c"};
public String[] getState(){
return states;
}
public static void main(String[] args) {
ExamplePublish1 sta = new ExamplePublish1();
log.info("{}", Arrays.toString(sta.getState()));
//模拟修改,该对象不安全
sta.getState()[0] = "d";
log.info("{}", Arrays.toString(sta.getState()));
}
}
对象逸出:一种错误的发布.当一个对象还没有构造完成时,就使它被其他线程所见.
package juc.publish;
import lombok.extern.slf4j.Slf4j;
//错误的发布方式
@Slf4j
public class ExamplePublish2 {
private int state = 0;
public ExamplePublish2(){
new InnerClass();
}
private class InnerClass{
public InnerClass(){
log.info("{}", ExamplePublish2.this.state);
}
}
public static void main(String[] args) {
new ExamplePublish2();
}
}
单例模式
package juc.singleton;
import juc.annotations.NotThreadSafe;
import lombok.extern.slf4j.Slf4j;
import java.net.Socket;
/**
* 单例模式-Double-Check+volatile
*/
@Slf4j
@NotThreadSafe
public final class Singleton1 {
// volatile保证有序性,socket的初始化发生在实例化之前
// JVM指令重排序和hepends-before规则,实例化和初始化并无前后关系约束,可能会出现空指针异常
private volatile static Singleton1 singleton = null;
private byte[] data = new byte[1024];
private Socket socket;
private Singleton1(){
// this.socket Socket初始化
}
public static Singleton1 getInstance(){
if(singleton ==null){
synchronized (Singleton1.class){
if (singleton == null){
singleton = new Singleton1();
}
}
}
return singleton;
}
}
package juc.singleton;
import juc.annotations.NotThreadSafe;
import lombok.extern.slf4j.Slf4j;
/**
* Holder方式
*/
@Slf4j
@NotThreadSafe
public class Singleton2 {
private byte[] data = new byte[1024];
// Singleton2类初始化的过程不会创建实例
private Singleton2(){
}
// Holder被引用的时候才会创建Singleton2实例
private static class Holder{
private static Singleton2 singleton = new Singleton2();
}
// Singleton2实例创建过程,在java程序编译时期收收集到<clinit>()方法中,该方法是同步的,能保证线程安全
private static Singleton2 getInstance(){
return Holder.singleton;
}
}
package juc.singleton;
import juc.annotations.NotThreadSafe;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@NotThreadSafe
public class Singleton3 {
private byte[] data = new byte[1024];
private Singleton3(){
}
/**
* 枚举类型不允许被继承,同样是线程安全的且只能被实例化一次
*/
private enum EnumHolder{
SINGLETON;
private Singleton3 singleton;
EnumHolder(){
this.singleton = new Singleton3();
}
private Singleton3 getInstance(){
return singleton;
}
}
public static Singleton3 getInstance(){
return EnumHolder.SINGLETON.getInstance();
}
}
不可变对象-final
修饰类:不能被继承
修饰方法: 该方法不能被继承
修改变量:基本数据类型不能更改,引用类型的内存地址不能更改
package juc.immutable;
import juc.annotations.NotThreadSafe;
import lombok.extern.slf4j.Slf4j;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@NotThreadSafe
public class ExampleFinal1 {
// final 定义引用类型,内存地址不能更改,k-v是可以更改的
private static final Map<String, String> map = new HashMap<>();
// 初始化
static{
map.put("a", "a");
map.put("b", "b");
map.put("c", "c");
}
public static void main(String[] args) {
map.put("a", "x");
log.info("{}", map.get("a"));
// map = new HashMap<>(); 错误,内存地址不能更改
}
}
Collections中定义了一些内部类,初始化之后的Set,Map等集合,不能更改内存地址,也不能更改内容
package juc.immutable;
import juc.annotations.NotThreadSafe;
import lombok.extern.slf4j.Slf4j;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@NotThreadSafe
public class ExampleFinal2 {
// final 定义引用类型,内存地址不能更改,k-v是可以更改的
private static Map<String, String> map = new HashMap<>();
// 初始化
static{
map.put("a", "a");
map.put("b", "b");
map.put("c", "c");
map = Collections.unmodifiableMap(map);
}
/**
* java.lang.UnsupportedOperationException异常抛出
* 原理是重构map中的方法,比如:直接抛出异常
*/
public static void main(String[] args) {
map.put("s", "x");
log.info("{}", map.get("a"));
}
}
线程封闭
Ad-hoc线程封闭:程序控制实现,最糟糕
堆栈封闭:局部变量,无并发问题
ThreadLocal线程封闭:特别好的封闭方法
分析ThreadLocal--------------省略
8 线程(不)安全类与写法
StringBuilder(不安全)-StringBuffer(安全)
package juc.concurrency;
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;
@Slf4j
@ThreadSafe
public class ExampleTest1 {
// 请求总数
private static final int clientTotal = 5000;
// 线程数
private static final int threadTotal = 50;
private static StringBuffer str = new StringBuffer();
private static void add(){
str.append("1");
}
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("length: {}", str.length());
executorService.shutdown();
}
}
package juc.concurrency;
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;
@Slf4j
@NotThreadSafe
public class ExampleTest1 {
// 请求总数
private static final int clientTotal = 5000;
// 线程数
private static final int threadTotal = 50;
private static StringBuilder str = new StringBuilder();
private static void add(){
str.append("1");
}
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("length: {}", str.length());
executorService.shutdown();
}
}
SimpleDateFormate(不安全)-jodaDateTime(安全)
package juc.concurrency;
import lombok.extern.slf4j.Slf4j;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
@Slf4j
@NotThreadSafe
public class ExampleTest1 {
// 请求总数
private static final int clientTotal = 5000;
// 线程数
private static final int threadTotal = 50;
// 错误的使用方式,每次使用SimpleDateFormat都得重新new对象
private static SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd");
private static void add() throws ParseException {
format.parse(format.format(new Date()));
}
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();
executorService.shutdown();
}
}
package juc.concurrency;
import juc.annotations.ThreadSafe;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import java.text.ParseException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
@Slf4j
@ThreadSafe
public class ExampleTest1 {
// 请求总数
private static final int clientTotal = 5000;
// 线程数
private static final int threadTotal = 50;
private static DateTimeFormatter format = DateTimeFormat.forPattern("yyyyMMdd");
private static void add() throws ParseException {
DateTime.parse("20180920", format);
}
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();
executorService.shutdown();
}
}
ArrayList-线程不安全,Vector-Stack-线程安全
JUC-CopyOnWriteArrayList-用于读多写少
public boolean add(E e) {
// 使用JUC下的lock锁
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
// 复制数组,然后执行写操作,次操作消耗内存
// 读操作是没有加锁的,就不能保证实时读获取数据
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
// 重新指向新数组
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
package juc.concurrency;
import juc.annotations.NotThreadSafe;
import lombok.extern.slf4j.Slf4j;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
@Slf4j
@NotThreadSafe
public class ExampleTest2 {
// 请求总数
private static final int clientTotal = 5000;
// 线程数
private static final int threadTotal = 50;
//private static List<String> list = new Vector<>(); //线程安全
private static List<String> list = new ArrayList<>(); //线程不安全
private static void add() throws ParseException {
list.add("aaa");
}
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("{}",list.size());
executorService.shutdown();
}
}
HashSet-线程不安全,TreeSet-线程不安全
JUC-CopyOnWriteArraySet,JUC-ConcurrentSkipListSet
package juc.concurrency;
import juc.annotations.NotThreadSafe;
import lombok.extern.slf4j.Slf4j;
import java.text.ParseException;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
@Slf4j
@NotThreadSafe
public class ExampleTest2 {
// 请求总数
private static final int clientTotal = 5000;
// 线程数
private static final int threadTotal = 50;
private static Set<Integer> set = new HashSet<>();
private static void add(int i) throws ParseException {
set.add(i);
}
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++) {
final int count = i;
executorService.execute(() -> {
try {
semaphore.acquire();
add(count);
semaphore.release();
} catch (Exception e) {
e.printStackTrace();
}
countDownLatch.countDown();
});
}
countDownLatch.await();
log.info("{}",set.size());
executorService.shutdown();
}
}
HashMap-线程不安全,TreeMap-(红黑树)线程不安全,Hashtable-线程安全
JUC-ConcurrentHashMap不允许空值,JUC-ConcurrentSkipListMap(跳表)
package juc.concurrency;
import juc.annotations.NotThreadSafe;
import lombok.extern.slf4j.Slf4j;
import java.text.ParseException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
@Slf4j
@NotThreadSafe
public class ExampleTest2 {
// 请求总数
private static final int clientTotal = 5000;
// 线程数
private static final int threadTotal = 50;
// private static Map<Integer, Integer> map = new Hashtable<>(); //线程安全
private static Map<Integer, Integer> map = new HashMap<>(); // 线程不安全
private static void add(int i) throws ParseException {
map.put(i,i);
}
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++) {
final int count = i;
executorService.execute(() -> {
try {
semaphore.acquire();
add(count);
semaphore.release();
} catch (Exception e) {
e.printStackTrace();
}
countDownLatch.countDown();
});
}
countDownLatch.await();
log.info("{}",map.size());
executorService.shutdown();
}
}
Collections类中定义的线程安全集合类
给集合操作的方法加了synchronized关键字
9 JUC的AQS并发器
AQS同步组件
CountDownLatch控制并发数量,一个等多个线程
Semaphore控制并发峰值
CyclicBarrier和CountDownLatch相似,计数可以重置.多个等多个线程
package juc.concurrency;
import juc.annotations.ThreadSafe;
import lombok.extern.slf4j.Slf4j;
import java.text.ParseException;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@Slf4j
@ThreadSafe
public class ExampleTest6 {
private static CyclicBarrier cyc = new CyclicBarrier(5, ()->{
log.info("callback is running");
});
private static void add(int a) throws ParseException, BrokenBarrierException, InterruptedException {
log.info("{} is ready", a);
cyc.await();
log.info("{} continue", a);
}
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newCachedThreadPool();
for(int i=0; i < 10; i++) {
int finalI = i;
Thread.sleep(1000);
executorService.execute(() -> {
try {
add(finalI);
} catch (Exception e) {
e.printStackTrace();
}
});
}
executorService.shutdown();
}
}
ReentrantLock有共享锁和独占锁,自旋锁
可重入锁,可指定公平锁还是非公平锁(默认)
提供能够中断等待锁的线程的机制
package juc.concurrency;
import juc.annotations.NotThreadSafe;
import lombok.extern.slf4j.Slf4j;
import java.text.ParseException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.ReentrantLock;
@Slf4j
@NotThreadSafe
public class ExampleTest7 {
// 请求总数
private static final int clientTotal = 5000;
// 线程数
private static final int threadTotal = 50;
private static int count = 0;
private static ReentrantLock lock = new ReentrantLock();
private static void add(int i) throws ParseException {
try {
lock.lock();
count++;
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
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(count);
semaphore.release();
} catch (Exception e) {
e.printStackTrace();
}
countDownLatch.countDown();
});
}
countDownLatch.await();
log.info("{}", count);
executorService.shutdown();
}
}
ReentrantReadWriteLock读写锁,写锁是排它锁(不能有读写锁),读多写少的时,写操作容易造成饥饿
package juc.concurrency;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ExampleTest8 {
private final Map<String,Data> map = new TreeMap<>();
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private final ReentrantReadWriteLock.ReadLock readLock = lock.readLock();
private final ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();
public Data get(String key){
readLock.lock();
try {
return map.get(key);
} finally {
readLock.unlock();
}
}
public Set<String> getAllKeys(){
readLock.lock();
try {
return map.keySet();
} finally {
readLock.unlock();
}
}
public Data put(String key, Data data){
writeLock.lock();
try {
return map.put(key, data);
} finally {
writeLock.unlock();
}
}
class Data{
}
}
StampedLock提供了乐观读锁,可取代ReadWriteLock以进一步提升并发性能
Condition分组唤醒需要唤醒的线程
package juc.concurrency;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
@Slf4j
public class ExampleTest9 {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();
new Thread(()->{
try {
lock.lock();
log.info("wait signal");
condition.await();
log.info("get signal");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}).start();
new Thread(()->{
try {
lock.lock();
log.info("get lock");
Thread.sleep(3000);
condition.signalAll();
log.info("send signal");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}).start();
}
}
Future和Callable
package juc.concurrency;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.*;
@Slf4j
public class ExampleTest10 {
static class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
log.info("is running");
Thread.sleep(3000);
return "is over";
}
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executor = Executors.newCachedThreadPool();
Future<String> future = executor.submit(new MyCallable());
Thread.sleep(2000);
String str = future.get();
log.info("{}", str);
}
}
FutureTask
package juc.concurrency;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.FutureTask;
@Slf4j
public class ExampleTest11 {
public static void main(String[] args) throws Exception{
FutureTask<String> futureTask = new FutureTask<>(() -> {
log.info("is running");
Thread.sleep(3000);
return "is over";
});
new Thread(futureTask).start();
Thread.sleep(2000);
String str = futureTask.get();
log.info("{}", str);
}
}
BlockingQueue阻塞队列
ArrayBlockingQueue先进先出,数组结构,容量限制的阻塞队列,队列不支持空元素,内部只有一把锁,意味着同一时刻只有一个线程能进行入队或者出队的操作
DelayQueue延迟队列提供了在指定时间才能获取队列元素的功能
LinkedBlockingQueue链表结构,先进先出,两把锁,一把用于入队,一把用于出队,有效的避免了入队与出队时使用一把锁带来的竞争
PriorityBlockingQueue可以插入null,有排序规则,实现Comparable的compareTo方法定义规则
SynchronousQueue只能有一个元素
9 线程池
【强制】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样 的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 说明:Executors 返回的线程池对象的弊端如下: 1)FixedThreadPool 和 SingleThreadPool: 允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。 2)CachedThreadPool 和 ScheduledThreadPool: 允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。
线程池的创建
new ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,long keepAliveTime, TimeUnit unit,BlockingQueue workQueue,RejectedExecutionHandler handler)
(1)corePoolSize: 线程池维护线程的最少数量 (core : 核心)
(2)maximumPoolSize: 线程池维护线程的最大数量
(3)keepAliveTime: 线程池维护线程所允许的空闲时间
(4)unit: 线程池维护线程所允许的空闲时间的单位
(5)workQueue: 线程池所使用的缓冲队列
(6)handler: 线程池对拒绝任务的处理策略
handler有四个选择:
ThreadPoolExecutor.AbortPolicy(): 抛出java.util.concurrent.RejectedExecutionException异常
ThreadPoolExecutor.CallerRunsPolicy(): 重试添加当前的任务,他会自动重复调用execute()方法
ThreadPoolExecutor.DiscardOldestPolicy(): 抛弃旧的任务
ThreadPoolExecutor.DiscardPolicy(): 抛弃当前的任务
线程池中添加任务
1.如果当前线程数小于corePoolSize,则创建新的线程处理任务
2.如果当前线程数等于corePoolSize,任务加入队列
3.如果当前线程数等于或者大于corePoolSize,队列满,则创建新的线程处理任务
4.如果当前线程数等于maximumPoolSize,队列满,则会执行handler处理策略
线程池-合理配置
CPU密集型任务,就需要尽量压榨CPU,参考值可以设为NCPU+1
IO密集型任务,参考值可以设置为2*NCPU
多线程并发最佳实践
- 使用本地变量
- 使用不可变类
- 最小化锁的作用域范围:S = 1/(1-a+a/n)
- 使用线程池,而不是直接new Thread
- 宁可使用同步(Samephore等)也不要使用线程的wait和notify
- 使用BlockingQueue实现生产-消费模式
- 使用并发集合而不是加了锁的同步集合
- 使用Semaphore创建有界的访问
- 宁可使用同步代码块,也不适用同步的方法
- 避免使用静态变量(final或者只读处理)