并发与高并发

本文探讨了并发与高并发的基本概念,强调了CPU缓存的一致性问题,并介绍了PostMan、JMeter在测试中的应用。同时,文章深入讲解了Java中的线程安全性、Atomic包、synchronized关键字以及线程池的创建与配置。最后提出了多线程并发的最佳实践建议。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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缓存有什么意义

  1. 时间局部性:如果某个数据被访问,那么不久的将来它很可能被再次访问
  2. 空间局部性:如果某个数据被访问,那么与它相邻的数据很快也可能被访问

CPU多级缓存-乱序执行优化
处理器为提高运算速度而做出违背代码原有顺序的优化,单核情况无影响,在多核情况下数据结果可能不一致.

2 CPU缓存一致性

在多线程的情况下,例如i++的操作,多个线程执行共1000次操作,结果很有可能小于10000.解决办法之一是使用缓存一致性协议,最出名的协议为MESI协议,MESI协议保证了每一个缓存中使用的共享变量副本都是一致的,当CPU在操作Cache中的数据时,如果发现该变量是一个共享变量,也就是其他Cache中也存在一个副本,那么:1)读取操作,不过任何处理,知识将Cache中的数据读取到寄存器.2)写入操作,发出信号通知其他CPU将该变量的Cache line置为无效状态,其他CPU在进行该变量读入的时候补得不到主内存中再次获取.

3 PostMan

POSTMAN

编写环境变量
http://localhost:8080可通过{{}}获取并且访问"/test"地址,这样可以统一环境.更加方便

4 JMeter使用

添加线程组
添加线程组
添加http请求
添加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等集合,不能更改内存地址,也不能更改内容
Collections

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关键字
Collections

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

多线程并发最佳实践

  1. 使用本地变量
  2. 使用不可变类
  3. 最小化锁的作用域范围:S = 1/(1-a+a/n)
  4. 使用线程池,而不是直接new Thread
  5. 宁可使用同步(Samephore等)也不要使用线程的wait和notify
  6. 使用BlockingQueue实现生产-消费模式
  7. 使用并发集合而不是加了锁的同步集合
  8. 使用Semaphore创建有界的访问
  9. 宁可使用同步代码块,也不适用同步的方法
  10. 避免使用静态变量(final或者只读处理)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值