zoukankan      html  css  js  c++  java
  • 多线程相关基础知识

    进程与线程区别:
    1、进程的创建与销毁大于线程;
    2、进程是操作系统分配资源的基本单位,线程是操作系统调度的基本单位;
    3、一个进程内可以有多个线程,一条线程只能存于一个进程内;
    4、进程之间资源分配是独立的,线程之间共享进程中的资源;

    概念:
    临界区:各个线程共享的资源,但每次只能有一条线程使用;
    死锁:线程之间互相等待对方的占用的资源,以至于无法进行下一步工作(线程状态不变);
    活锁:线程之间互相谦让,以至于无法进行下一步工作(线程状态会变);
    饥饿:线程无法获得所需的资源;

    并发级别:
    阻塞:在某线程释放资源之前,其它线程无法继续执行;(悲观策略,认为冲突一定会产生)
    无饥饿:各条线程都有机会执行,先来后到、公平锁;
    无障碍:不会阻塞,但共享数据发生了冲突,则会把相关线程所做修改回滚;(乐观策略,认为冲突不一定会产生)
    无锁:所有线程都可以对临界区访问,会有一个无穷循环,线程会一直访问临界区,直到没有冲突,修改成功;
    无等待:不会被锁定,有限步完成,比如先读数据,在适时的时候写回;

    达到线程安全的三条性质:
    原子性:一个操作是不可中断的,要么全部执行成功要么全部执行失败,有着“同生共死”的感觉;
    可见性:指当一个线程修改了共享变量后,其他线程能够立即得知这个修改;
    有序性:为了提高性能,编译器和处理器常常会对指令进行重排序,但在单线程里最终执行结果不变;

    两个核心:
    JMM;
    Happen-Before规则:Java虚拟机和执行系统会对指令重排序,但也是有一定规则不能违背的;

     线程实现:

    1、继承Thread;
    2、实现Runnable接口;
    3、用Future、Callable,带返回值地创建;
    4、使用线程池创建;

    线程生命周期:
    新建、就绪、运行、阻塞、死亡;(其中就绪、运行、阻塞可以相互转化)

    终止线程和线程中断:
    终止:线程对象.stop() 直接停止线程;
    中断:线程对象.interrupt() 标记中断,
         当前线程.isInterrupted() 判断线程是否有中断标记,
         当前线程.interrupted() 判断线程是否有中断标记,有则清除标记;

     关键字:

    Object中的:
    等待和通知:wait(交出锁)、notify/notifyAll;
    Thread中的:
    join(等待该线程结束)、yield(当前线程让出CPU,重新去竞争)、sleep(暂停,不交出锁);

    线程其它相关:
    1、线程组:对线程组操作,相当于对线程组里的所有线程操作;
    2、守护线程:用户线程结束后,守护线程就会结束;
    3、线程优先级:高优先级有更大的概率获取资源;

    锁:
    1、synchronized:Java的原生锁,可以用在类、方法、代码块、变量、对象上;
    2、重入锁(ReentrantLock):可以多次获得同一把锁,但同时也需要同样多次释放锁;
      重入锁可以有中断响应、锁等待时限、设置公平锁;
      公平锁:即线程按先来后到的顺序获取资源;
      使用Condition,类似wait、notify;
    3、读写锁(ReadWriteLock):线程间允许同时读(非阻塞),但多线程间(读、写),(写、写)操作是阻塞的;
    4、信号量:允许指定多条线程进入临界区;
    5、计数器(CountDownLatch):即指定多条线程完成某样工作,当前线程才继续执行(执行完一条线程,计数器就-1,不可复用);
    6、循环栅栏(CyclicBarrier):即指定多条线程完成某样工作,这些线程就会暂停,等待再次唤醒执行(可复用);
      计数器与循环栅栏对比:
        0、可复用和不可复用;
        1、计数器 指定的线程运行到了计算器处,计数器就-1,该线程还是会执行后序操作;
        2、循环栅栏 等待所有指定线程运行到循环栅栏处,这些线程就会挂起;

    线程池:
    1、固定线程池内线程数;newFixedThreadPool
    2、单一线程的线程池;newSingleThreadExecutor
    3、不确定条数线程的线程池;newCacheThreadPool
    4、自定义核心线程数和最大线程数的线程池;ThreadPoolExecutor
    5、定时任务,可以指定每条线程执行任务的间隔(scheduleAtFixeRate、scheduleWithFixedDelay);
      scheduleAtFixeRate间隔为指定间隔;
      scheduleWithFixedDelay间隔为指定间隔+任务执行时间;

    6、以上其中1、2、3的底层都是由4实现的
    ThreadPoolExecutor构造函数有以下参数:
      corePoolSize:表示核心线程数(一直存活的);
      maximumPoolSize:表示线程池里最大线程数(当核心线程数不够用时);
      keepAliveTime:表示除了核心线程之外的其它线程,可以存活多久;
      unit:keepAliveTime的时间单位;
      workQueue:任务队列,在等待执行的任务;
      threadFactory:线程工厂,用来创建线程;
      handler:拒绝策略(线程池已经达到最大数量、任务队列也满了时,所做的操作);

    7、线程增强:
    线程池可以为每条线程编写线程执行前置方法(beforeExecure)、结束方法(afterExecute)、线程池结束方法(terminated);

    8、使用线程池,开启线程的方法:
    线程池实例.submit 、 线程池实例.execute()
      二者不同:
        submit有返回值,会把返回结果封装到Future<?>中,其中异常信息也会包含在Future<?>中;
        execute无返回值,有异常会直接抛出;

    9、Fork/Join
    可以把一个大任务,用线程分成若干个小任务(Fork),最后再把结果合并(Join);

    Java虚拟机中的锁优化:
    1、偏向锁:线程ID记录在对象头中(Mark Word),每次只需检查ID相符就可以直接进入代码块,若ID不同则说明有竞争,偏向锁 失效;
    2、轻量锁:在栈帧中起一个锁记录,记录对象头;
      每次线程获取锁需要去更新对象头中指向栈帧中的记录,若成功则进入同步代码块;
      若失败,则判断对象头部是否已经指向了当前线程所在的栈当中,是则继续执行;
      若否则说明有多条线程竞争,膨胀为重量锁;

      偏向锁只适用于在只有一个线程执行同步代码块的情况;
      轻量级锁的“轻”在于当没有多线程竞争的情况下,只利用简单的CAS操作来代替操作系统互斥量在减少开销,所以要轻;

    3、自旋锁:挂起再唤醒需要更多的消耗,让当前线程做几个空循环,可能过一会它就获得了锁;
    4、锁消除:去除不能存在共享资源竞争的锁;

    线程局部变量(ThreadLocal):
    1、适用于线程间不用共享的,但每个线程都需要用到的资源;若使用一个普通全局变量,容易在多线程下被写乱;
    2、ThreadLocal内部类似一个Map,键为线程本身,值为对应的资源,所以不同线程访问到是不同的资源;

    无锁:
    1、使用CAS(比较替换)达到无锁;
    2、即需要三个参数,‘这个变量现在应该是怎么样的(预期值)’、‘这个值(当前值)’、‘需要赋予的新值(新值)’;
    3、即比较预期值和当前值,若相等,则把当前值更新为新值;不相等,则说明被其它线程修改过了,得重新获取;
    4、使用JDK并发包中的atomic包来进行CAS操作;

    等待队列SynchronousQueue:
    1、该队列容量为0,在线程中使用该队列,放入一个资源,该线程就会阻塞,直到该资源被另一线程取走;
    2、容量为0不是指不能放数据或只能放一个数据,而是指配对通信机制;

    单例模式:
    1、单线程下的:私有构造函数,私有对象字段,静态的get,判断对象有无实例化,有则直接返回,无则实例后返回;
    2、多线程1:私有构造函数,静态的对象并实例化,静态的get,(因为在JVM中,静态变量会在类加载时实例);
    3、多线程2:私有构造函数,双重判断(先判断是否实例,无则加锁,在判断一次是否实例);
    4、多线程3:私有构造函数,2会过早实例化,3太麻烦效率不高;使用一个内部类,需要返回的对象写内部类里,并静态修饰;

    生产者-消费者模式:
    1、即部分线程负责提交资源和数据,部分线程负责处理;
    2、两部分线程可能并不同步(一个快一些,一个慢一些),所以需要一个中间的缓冲区来存储数据,以供生产者存入数据,消费者 取数据;
    3、使用阻塞队列BlockigQueue作为中间缓冲区;
    4、使用无锁的缓存框架Disruptor;

    Future模式:
    1、当数据提交给线程之后,线程会直接返回一个虚拟数据;
    2、如果去获取这个虚拟数据的话,线程就会阻塞,直到真实数据准备好;
    3、这样做的好处是,提交了数据之后,当前线程可以先执行其它任务,不必一直等待当前任务;
    4、整个过程就像网购下单付款后,立刻收到的只是一个订单,快递得过几天才到,但这期间我们可以干其它事;
    5、JDK中的Future模式(Callable<>接口,Future<>获取结果)

    并行流水线:
    1、适用于各条线程之间有很强的联系关系,该例子是要计算(B+C)*B/2,但数据在不同线程;
    2、将算式分解为:thread1:A=B+C thread2:D=A*B thread3:D=D/2
    3、流程:B、C数据进入线程1处理,线程1把A数据传递给线程2处理,线程2把D数据传递给线程3处理;
    4、在此期间,当A数据到达线程2时,线程1又可以处理一个新的数据......

  • 相关阅读:
    TOMCAT热部署 catalina.home catalina.base
    spring boot test MockBean
    源码分析ConcurrentHashMap
    源码分析Thread
    源码分析String
    jvm 占用高的问题定位
    docker 资源限制
    数据库设计方案与优化
    linux搜索查找类命令|--grep指令
    linux搜索查找类命令|--locate命令
  • 原文地址:https://www.cnblogs.com/Drajun/p/8489071.html
Copyright © 2011-2022 走看看