多线程

发布时间 2023-09-25 21:48:21作者: deigang

首先先说一下什么是进程什么事线程,进程就是在内存中正在运行的程序,线程是进程的最小执行单位,一个进程最少得有一个线程,线程是指软件中的每一个功能。

     线程的创建方式有三种,继承Thread类,实现Runable接口,实现Callable接口,继承Thread类和实现Runable接口的区别在于,java是单继承的如果使用继承Thread的方法在线程链路特别长的时候会造成调用时长过长降低效率,是如果实现了Runable接口的话,则很容易的实现资源共享。实现Runable接口和实现Callable接口的区别在于,Runnable 接口 run 方法无返回值;Callable 接口 call 方法有返回 值,是个泛型, Runnable 接口 run 方法只能抛出运行时异常,且无法捕获处理; Callable 接口 call 方法允许抛出异常,可以获取异常信息。

      线程池的核心参数,核心线程数,最大线程数,阻塞队列,临时线程存活时间,存活单位,线程工厂,拒绝策略,拒绝策略分为:丢弃任务并抛出,丢弃任务,但是不抛出异常。如果线程队列已满,则后续提交的任务都会被丢弃,且是静默丢弃,丢弃队列最前面的任务,然后重新提交被拒绝的任务,由调用线程处理该任务。

       线程的工作流程,当有任务提交时,看是否达到核心线程数量?没达到,创建-个工作线程来执行任务。在看工作队列是否已满? 没满,则将新提交的任务存储在工作队列里。最后看是否达到线程池最大数量?没达到,则创建一个新的工作线程来执行任务最后,执行拒绝策略来处理这个任务。

       线程池的状态分为,新建,就绪,运行,阻塞,死亡,线程之间的通讯分为同步,异步,以及进程间的通讯,同步通讯可以使用jion,wait,notify,notifyall,和volatile关键字来进行,进程间的通讯就可以使用feign接口进行。

线程安全的特性,原子性一个线程操作是不能被其他线程打断,有序性线程在执行程序是有序的,可见性一个线程修改数据后,对其他线程是可见的,

volatile

       保证可见性,可以在一个线程修改了共享数据时对其他线程可见,其原理就是修改了变量副本值后及时同步到主内存,其他线程从主内存中获取

       保证有序性,会禁止指令重排,指令重排:Java代码翻译成class文件后,最终在JVM执行时,是把class翻译一个个的指令进行执行的。而CPU为了程序的执行性能,会对指令进行重新排序也就是说万一翻译后的指令是123,那么重排后的指令可能就是213。在多线程情况下,就会出现变量的可见性问题

       volatile是基于 内存屏障 来保证

有序性

                     内存屏障(也称内存栅栏,内存栅障,屏障指令等,是一类同步屏障指令,是CPU或编译器在对内存随机访问的操作中的一个同步点,使得此点之前的所有读写操作都执行后才可以开始执行此点之后的操作),避免代码重排序。内存屏障提供了避免重排序的功能

              可见性

                     内存屏障之前的所有写操作都要回写到主内存,内存屏障之后的所有读操作都能获得内存屏障之前的所有写操作的最新结果(实现了可见性)

                     内存屏障会把线程把工作内存中改变后的数据直接刷回主内存。其他线程就可以从主内存中获取最新数据

 

线程安全问题,也就是共享数据不安全

      

解决方案的话就是上锁,

              Synchronized 与 Lock 锁的区别

                            Synchronized

                                   JVM层面、是关键字

                                   出异常时会释放锁,不会出现死锁

                                   不会手动释放锁,只能等同步代码块或方法结束后释放锁

                            Lock

                                   API层面、是接口

                                   出异常时不会释放锁,会出现死锁,需要在finally中手动释放锁

                                   可以调用api手动释放锁

                     Synchronized

                            普通同步方法,锁是当前实例对象 this

                            静态同步方法,锁是当前类的class对象

                            同步代码块,锁是括号里面的对象【必须共享】

                            底层使用 monitorenter 和 monitorexit  指令实现的

              锁分类

                     悲观锁与乐观锁

                            悲观锁Synchronized,Lock,数据库的行锁、表锁也是悲观锁

                            乐观锁的典型代表就是cas,一般在数据库使用version字段用来解决乐观锁的数据不一致的情况。

              公平锁与非公平锁

                            公平锁:按线程顺序获得锁

                            非公平锁:线程随机获得锁

              死锁

                     死锁产生的原因

                            1、系统资源不足;

                            2、进程运行推进的次序不合适;

                            3、资源分配不当。

                            4、如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就很低,否则就会因争夺有限的资源而陷入死锁。其次,进程运行推进顺序与速度不同,也可能产生死锁。

       死锁产生的原因及四个必要条件

              互斥条件:一个资源一次只能被一个进程访问。

              请求与保持: 一个进程因请求资源而阻塞时,对已获得的资源保持不放。

              不可剥夺:进程已获得的资源,在未使用完之前,不得强行剥夺。

              循环等待:若干进程之间形成一种头尾相接的循环等待资源关系