JAVA多线程与高并发

基本概念

线程实现

  • 继承Thread
  • 实现Runnable接口
    其他如FutureTask、lambda 表达式都是基于以上两种方式的包装或扩展

线程基本方法

  • sleep
  • yield(让出一轮cpu时间片)
  • join 等待当前线程完成
  • interrupt——设置中止标志
    • interrupted:查询中止状态并重置
    • isInterrupted:查询中止状态
    • 如果线程被join、wait、sleep阻塞,设置阻塞标志后将抛出异常,并重置标志
    • 如果线程被阻塞在InterruptibleChannel上,将抛出异常
    • 如果线程被阻塞在Selector上,线程将立即返回,类似调用Selector的wakeup方法

线程状态

start
(sleep、join、wait等)
New
Ready
Running
Blocked
Teminated

线程同步

synchronized

用法
  • synchronized方法
    实例方法等同于synchronized(this)
    静态方法等同于synchronized(T.class)
  • synchronized代码块
特性
  • 原子性
  • 可见性
  • 可重入性
  • 禁止指令重排序
  • 异常的锁——程序中出现异常,锁会被释放
底层实现(锁升级)
注意事项
  • == 不能使用String和基础数据类型的装箱类型==
  • 禁止修改锁的对象(修改对象引用)
public class D01 implements Runnable {
    private  Object lock=new Object();
    private  int count;
    public static void main(String[] args) {
        int size=20;
        D01 d01 = new D01();
        Thread[] t=new Thread[size];
        for (int i = 0; i < size; i++) {
            t[i]=new Thread(d01, "t"+i);
        }
      
        System.out.println("启动线程。。。。 " );
        for (int i = 0; i <size ; i++) {
            t[i].start();
        }

        for (int i = 0; i <50 ; i++) {
            d01.modifyLock();
            try {
                Thread.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        // d01.printCount();

    }

    public D01(){
        this.count=0;
    }
    public void modifyLock(){
        lock=new D01();
        System.out.println("修改锁对象引用。。。。 " );
    }

    public  void printCount(){
        System.out.println("thread:"+ Thread.currentThread().getName()+ "count = " + count);
    }
    @Override
    public void run() {
        synchronized (lock){

            for (int i = 0; i <100_0000 ; i++) {
                count++;
            }
            printCount();

        }

    }
}

volatile

特性
  • 线程之间可见性——缓存一致性协议
  • 禁止指令重排序——内存屏障实现
  • 不保证原子性

CAS

  • compare and set/swap
  • CPU指令支持
  • ABA问题,加上版本号解决
    线程1 x:a->b
    线程2 x:b->a
    线程1把变量值由A改为B,然后线程2又把变量值由B改成A

AQS(AbstractQueuedSynchronizer)

概况
  • 为基于FIFO等待队列的同步器提供了基本的框架实现
  • 适用于依赖单原子值(atomic int value)来表示状态(获得/释放锁)的同步器,子类负责维护state字段
  • 支持互斥锁和共享锁
  • 核心是变种的CLH自旋锁
  • 使用varhandle更新对象字段值(jdk>=1.9)
CLH自旋锁
  • 线程等待分为被动等待和主动等待(自旋),被动等待注册锁请求,锁释放后,注册的线程就会被唤醒;主动等待就是不停的的检测锁的状态,直到竞争到锁。直观感觉,自旋肯定比被动等待浪费性能,但是对于小任务,空转时间比较短,反而比被动等待的代价小,CLH锁就是一种自旋锁,它在本地变量上自旋,不断检查前驱状态
  • CLH采用FIFO的单向链表结构,但是AQS使用的是CLH的一种变体,采用双向链表,pre链用于线程被取消;next链用于阻塞实现,当通知后续节点时,当后续节点为空时,通过原子更新tail指针来避免与新加入的的节点竞争
  • 自旋后升级为LockSuport.park
    CLH队列

wait/notify

  • wait必须先拥有锁,然后释放锁,等待其他线程通过notify/notifyall 唤醒,重新获取锁,才能恢复执行,一般与synchronized一起使用
  • notify唤醒通过wait被阻碍的线程,被唤醒的线程需要当前线程释放锁,然后重新获得锁才能继续执行,如果存在多个被阻塞的线程,线程的选择将依赖于系统的调度策略
public class WaitDemo {
    private static Object lock = new Object();
    public static void main(String[] args) {


        Thread t1=new Thread(()->{

            try {
                synchronized (lock){
                    System.out.println("t1 run....." );
                    Thread.sleep(5000);
                    lock.notify();
                    System.out.println("t1 notify and sleep....." );
                    Thread.sleep(5000);

                }

            } catch (Exception e) {
                e.printStackTrace();
            }

        });

        Thread t2=new Thread(()->{

            try {
                synchronized (lock){
                    System.out.println("t2 lock wait....." );
                    lock.wait();
                    System.out.println("t2 pass wait and sleep....." );
                    Thread.sleep(5000);

                }
                System.out.println("t2 run after wait....." );
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        });

        t2.start();
        t1.start();
    }
}

AtomicXXX

  • CAS

LongAdder

  • 内部使用分段锁

LockSupport

  • 创建其他同步器的基础类
  • 主要方法位park/unpark

ReentrantLock

  • AQS
  • 具有公平锁和非公平锁,对于公平锁,如果存在阻塞队列,则加入阻塞队列,否则直接竞争

ReadWriteLock

  • AQS
  • 共享排他锁

Semaphore

  • 限制访问资源的线程数量
  • AQS

Exchange

  • 适用于遗传算法和管道设计
  • dual data structure
public class ExchangDemo {
    public static void main(String[] args) {
        Exchanger<ArrayList<Integer>> exchanger = new Exchanger<ArrayList<Integer>>();
        Runnable writerTask= new Runnable() {
            @Override
            public void run() {
                ArrayList<Integer> writerList = new ArrayList<>(10);
                Random random = new Random();
                while (true){
                    if(writerList.size()>=10){
                        try {
                            System.out.println("exchanger..... ");
                             writerList = exchanger.exchange(writerList);
                             Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    else{
                        int i = random.nextInt();
                        System.out.println("add data " + i);
                        writerList.add(i);
                    }
                }
            }
        };

        Runnable readTask= new Runnable() {
            @Override
            public void run() {
                ArrayList<Integer> readerList = new ArrayList<>(10);

                while (true){
                    if(readerList.isEmpty()){
                        try {
                            readerList=exchanger.exchange(readerList);
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    else{
                       System.out.println("remove data " + readerList.remove(0));
                    }
                }
            }
        };
        new Thread(readTask).start();
        new Thread(writerTask).start();

    }
}

CountDownLatch

  • await/coundDown
  • AQS
  • 参与数不可变,不可重用

CyclicBarrier

  • reentranlock
  • 参与数不可变,可重用
  • 采用all-or-none模型,要么全部成功,要么全部失败,即一个线程在await之前中止、失败或超时,其他await的线程将中止
  • barrier action由最后到达barrier point的线程执行
public class CBDemo {
    public static void main(String[] args) {

        CyclicBarrier cyclicBarrier = new CyclicBarrier(10,()->{
            System.out.println(" cyclicbarrier action run .... in "+ Thread.currentThread().getName());
        });

        int i1 = new Random().nextInt(10);
        String threadName="thread"+i1;
        System.out.println("execption threadName = " + threadName);


        Thread[] threads = new Thread[10];
        for (int i = 0; i <10 ; i++) {
            threads[i]=new Thread(()->{
                System.out.println("process thread task.... in "+Thread.currentThread().getName());
               // if(threadName.equals(Thread.currentThread().getName())){
               //    int r= 1/0;
               // }
                try {
                    Thread.sleep(new Random().nextInt(500));
                    if(cyclicBarrier.await()==0){
                        System.out.println("exec barrier point task....");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }

            },"thread"+i);
        }

        for (Thread thread : threads) {
            thread.start();
        }

    }
}

Phaser

  • ManagedBlocker
  • 可重用,参与的parties 数可变
  • 当存在线程中止时,不会导致其他wait的线程也被中止,可以执行响应的恢复任务
  • phaser支持树形结构,通过分组减少竞争,提高吞吐量
public class PhaseDemo {

   public static void main(String[] args) throws InterruptedException {
      cleanHouse();

   }


   private  static  void cleanHouse(){

       Phaser phaserMain = new Phaser(1);
       Phaser phaserWindow = new Phaser(phaserMain);
       Phaser phaserDesk = new Phaser(phaserMain);

       Runnable taskClearDesk = new Runnable(){

           @Override
           public void run() {


               try {
                   Thread.sleep(new Random().nextInt(2000));
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
               System.out.println("我擦完桌子...in " + Thread.currentThread().getName());
               phaserDesk.arriveAndDeregister();
           }
       };

       Runnable taskClearWindow = new Runnable(){

           @Override
           public void run() {

               try {
                   Thread.sleep(new Random().nextInt(2000));
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
               System.out.println("我擦完窗户...in " + Thread.currentThread().getName());
               phaserWindow.arriveAndDeregister();
           }
       };

       Runnable taskClearFloor = new Runnable(){

           @Override
           public void run() {
               //phaserMain.awaitAdvance(0);

               try {
                   Thread.sleep(new Random().nextInt(2000));
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
               System.out.println("我扫完地...in " + Thread.currentThread().getName());
               phaserMain.arriveAndDeregister();
           }
       };
       Runnable taskClearRubbish = new Runnable(){

           @Override
           public void run() {
               //phaserMain.awaitAdvance(1);

               System.out.println("我扔完垃圾...in " + Thread.currentThread().getName());
               try {

                   Thread.sleep(new Random().nextInt(2000));
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
               phaserMain.arriveAndDeregister();
           }
       };





       for (int i = 0; i <4 ; i++) {
           phaserWindow.register();
           new Thread(taskClearWindow).start();
       }

       for (int i = 0; i <6 ; i++) {
           phaserDesk.register();
           new Thread(taskClearDesk).start();
       }
       phaserMain.arriveAndAwaitAdvance();

       for (int i = 0; i <8 ; i++) {
           phaserMain.register();
           new Thread(taskClearFloor).start();
       }
       phaserMain.arriveAndAwaitAdvance();

       for (int i = 0; i <3 ; i++) {
           phaserMain.register();
           new Thread(taskClearRubbish).start();
       }

       phaserMain.arriveAndAwaitAdvance();
       System.out.println("房屋打扫完成。。。。。");


   }
}

锁的选择

  • 执行时间比较短线程数少用自旋锁
  • 执行时间比较长线程数多用系统锁

锁的优化

  • 锁定细化——竞争激烈的情况下,尽量锁定关键的临界代码
  • 锁的膨胀——频繁的获取锁,很消耗资源,可以把相应的代码合并

ThreadLocal

引用类型
  • 强——没有引用就会被回收
  • 弱——当WeakReference 引用的对象,没有其他强引用时,它不会阻止gc回收改对象
  • 软——SoftReference 内存不够用时,被回收,主要用于缓存
  • 虚引用——PhantomReference 管理堆外内存
关键点
  • ThreadLocal基本上是个代理类,set/get都是操作的当前线程的ThreadLocalMap属性
  • ThreadLocalMap 以ThreadLocal为key,同时ThreadLocalMap中的Entry扩展自WeakRefercence,所以当ThreadLocal实例无其他外部引用时,该key会被回收,对应的value 无法访问,从而会出现内存泄漏,所以set对象用完后,remove(是否value也可以设计成弱引用?
  • ThreadLocalMap中是以ThreadLocal的HashCode值取低位(容量长度-1位)来定位Hash桶的,而该hash值是魔数(0x61c88647)的n-1倍,为什么要使用0x61c88647?因为基于斐波那契哈希方法( Fibonacci Hashing)的散列值分布比较均匀

h ( x ) = ⌊ M W ( a x m o d    W ) ⌋ M = 2 k W = 2 w a = ϕ − 1 W ϕ = 1 + 5 2 golden ratio(黄金比率) h(x)=\lfloor\frac M W(ax \mod W)\rfloor \\ M=2^k \\ W=2^w \\ a=\phi ^{-1}W \\ \phi=\frac {1+\sqrt5} 2 \tag*{golden ratio(黄金比率)} h(x)=WM(axmodW)M=2kW=2wa=ϕ1Wϕ=21+5 golden ratio(黄金比率)

  • ThreadLocalMap内部使用数组来存储Hash桶,容量为2^n,当实际存储量达到容量的三分之二时,将进行扩容
  • 使用开放定址法解决hash冲突

同步容器

容器概览

java集合概览

明细

List

  • CopyOnWriteArrayList
  1. 线程安全的ArrayList变体
  2. 任何对数组的改变(添加、删除)都会生成新的数组
  3. 迭代器(iterator)只是数组的一个镜像,不会反应数组的实时变化,同时迭代器也不支持增删等改变数组的操作
  4. 适合于读多写少的应用场景

Queue

BlockingQueue
  • 添加等待队列非空的take和等待队列有存储空间的put操作
  • 不支持插入null元素
  • 是否存在容量限制依据具体实现
  • 排队和出队都是线程安全的,但是批量操作(addAll、containsAll、retainAll和removeAll)并不一定是线程安全的,依赖于具体的实现
  • 主要用于生产者-消费者应用场景
  • happen-before原则:一个线程的入队操作优先于另外一个线程的出队(获取元素或删除元素)操作

下表为当队列满或空时,相应的入队或出队操作的结果:

抛出异常返回特别值阻塞超时
插入addofferputoffer
删除removepolltakepoll
检索而不删除elementpeek
  1. ArrayBlockingQueue
    1.1 有界队列
    1.2 入队和出队使用一把锁
  2. ConcurrentLinkedQueue
    2.1 使用无等待算法,内部使用CAS锁
    2.2 由于是无界队列,所以不存在入队及出队阻塞问题(无put/take方法)
    2.3 size属性可能不准确,因为需要遍历整个链表来获取size,而同时可能存在出入对操作
    2.4 队列中的头和尾指针是延后更新的(入队和出队并不会同时更新head和tail指针)
    2.5 采用单链表结构,头部存在一个哨兵节点(降低读写竞争)
  3. DelayQueue
    线程安全的PriorityQueue
  4. LinkedBlockingDeque
    4.1 双向链表,近似无界
    4.2 采用单锁,可双向操作
  5. LinkedBlockingQueue
    5.1 采用单链表结构,头部存在哨兵节点(降低读写竞争),近似无界
    5.2 采用双锁算法,读写两把锁
  6. LinkedTransferQueue
    6.1 采用双队列(dual queue),队列中包括请求节点和数据节点
    6.2 适用于生产者需要确认消费者收到消息
  7. PriorityBlockingQueue
    7.1 单锁
    7.2 采用基于数组的堆排序算法
  8. SynchronousQueue
    8.1 采用双队列
    8.2 生产者与消费者同步的,生产者等待消费才能继续,消费者等待生产者才能继续
    8.3 适合于管道等类似场景应用
    8.4 采用自旋加park方式阻塞

Set

  1. ConcurrentSkipListSet
  • 基于ConcurrentSkipListMap的Set
  1. CopyOnWriteArraySet
  • 基于CopyOnWriteArrayList的Set
  • 具有CopyOnWriteArrayList相似的特点

Map

ConcurrentMap
  1. ConcurrentHashMap
    1.1 内部采用数组实现,当出现Hash冲突时,相同Hash则以链表存储,数量超过8则转换为红黑树
    1.2 数组的大小为2n,当空间使用超过75%,则按2n+1进行扩容
    1.3 在更新时,如出现Hash冲突,会使用synchronized锁住该Hash值链上的第一个节点
    1.4 主要应用于读多写少场景,保证并发读的同时尽量减小数据更新竞争影响
    1.5 能提前预估数量容量,将会减少扩容的次数

  2. ConcurrentSkipListMap
    2.1 基于链表的跳表变体
    跳表

    2.2 25%的节点被索引,即增加层级的概率为0.25(随机数与0x80000001与等于0,排除负数(负数高位为1)和奇数)
    2.3 查询和更新的时间复杂度均为logn
    2.4 节点删除采用懒删除,先标记逻辑删除,最后物理删除

线程池

线程池作用

  • 任务调度优化
  • 异步任务跟踪
  • 资源管理

线程相关接口

任务接口

  • 无返回值——Runnable
  • 有返回值——Callable

线程池接口

  • 负责任务调度——Executor
  • ExecutorService
    是对Executor接口的扩展,提供了生命周期管理(shutdown)以及异步任务的跟踪

异步接口

  • 异步任务的执行结果——Future

  • FutureTask
    同时实现了Runnable和Future接口,一个可以被执行的Future

  • CompletableFuture
    一个可以显示设置任务的计算结果及状态的Future,同时支持当任务完成时,触发相应的操作

线程池分类

ThreadPoolExecutor

参数说明
核心线程数最大线程数线程创建工厂线程空闲时间及单位任务队列任务拒绝策略
corePoolSizemaximumPoolSizethreadFactorykeepAliveTime/unitworkQueuehandler
线程池中一致保持的线程数量,当当前线程小于核心线程数,新到任务将创建新的线程处理线程池最大的线程数量,当大于核心线程小于最大线程数时,如果队列未满,新到任务排队,如果队列已满,创建新的线程处理当所有线程都忙并且任务队列已满,新到任务的处理方式,主要抛异常、调用者运行、丢弃、丢弃最老的、自定义
特点

单队列

ForkJoinPool

  • 多队列
  • 任务分解(fork)与汇总(join)
  • 用很小的线程可以执行很多任务
  • 计算密集型

工厂类Executors

ThreadPoolExecutor 类
  • newSingleThreadExecutor
  • newFixedThreadPool
  • newCachedThreadPool
  • newScheduledThreadPool
ForkJoinPool类
  • newWorkStealingPool
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值