多线程面试级理解

  1. 多线程

    1. 解决多线程安全

      1. 乐观锁悲观锁

        1. 乐观锁:每次拿数据时候都认为别人不会修改,期间不上锁,但是在更新数据的时候判断一下在此期间有没有人修改过数据。一般通过版本号和cas算法实现。乐观锁适用于多读的使用,提高吞吐量。

        2. 悲观锁:每次拿数据都会认为别人会修改,期间上锁,其他访问如果想拿数据则需要阻塞等待。synchronized和lock锁都是悲观锁。多写使用悲观锁。

      2. CAS

        1. 轻量级同步机制

        2. 主要用于实现多线程环境下的无锁算法和数据结构,保证并发安全性。可以在不使用锁的情况下,对共享数据进行线程安全的操作。

        3. 主要参数:要更新的内存位置,期望的值和新值。

        4. 执行过程

          1. 获取要更新的内存位置var

          2. 将期望值expected与var比较,两者相等,内存位置的值var更新为新值new

          3. 两者不相等,则说明有其他线程修改了内存位置的值var。cas失败,重新尝试

        5. java中的实现类:Unsafe

        6. ABA问题

          1. ABA问题指在CAS操作过程中,如果变量的值被改为了A,B,再改回A,而CAS操作是能够成功的,这时候可能导致程序出现意外结果,在高并发场景下,使用CAS操作可能存在ABA问题,也就是在一个值被修改之前,先被其他线程修改为另外的值再被修改回原值,瓷实CAS操作会认为这个值没有被修改过,导致数据不一致。

          2. 解决:java提供了AtomicStampedReference类,类中使用版本号来控制解决ABA问题,每个共享变量都会关联一个版本号

        7. CPU空转

          1. CAS自旋时间过长,如果某个线程一直自旋等待,会浪费线程资源

          2. 解决:自适应自旋锁的方式,前几次重试等待,后面使用阻塞等待。

            1. 当一个线程请求获取锁时,如果当前线程已经持有锁,则将计数器加1,否则使用CAS操作来获取锁。这样可以避免了使用synchronized关键字或者ReentrantLock等锁的实现机制。

            2. 当线程获取锁失败时,使用自旋等待的方式,这样可以避免线程进入阻塞状态,避免了线程上下文切换的开销。当重试次数小于10时,使用自旋等待的方式,当重试次数大于10时,则使用阻塞等待的方式。这样可以在多线程环境下保证线程的公平性和效率。

            3. 在释放锁时,如果计数器大于0,则将计数器减1,否则将锁的拥有者设为null,唤醒其他线程。这样可以确保在有多个线程持有锁的情况下,正确释放锁资源,并唤醒其他等待线程,保证线程的正确性和公平性。

        8. cas应用场景

          1. 通常用于乐观锁和无锁算法

            1. 线程安全计数器

            2. 队列

            3. 数据库并发控制

            4. 自旋锁

            5. 线程池

      3. synchronized锁:

        1. 使用方式:修饰实例方法,修饰静态方法,修饰代码块

        2. 讲解:synchronized锁是用来解决线程安全问题的,被该锁修饰的代码块或方法任意时刻都只能有一个线程去执行。jdk1.6版本之前是重量级锁,之后锁升级,加了偏向锁,自旋锁,适应性自旋锁,轻量级锁,锁粗化等。

        3. 该锁是存在锁升级的:当线程的量达到临界区,如果锁存在竞争,则会升级为轻量级锁,自旋锁和自适应自旋锁。自旋锁的自旋次数达到设置的次数还没有获取到锁时,则会升级为重量级锁。

        4. 重量级锁耗费资源,因为线程获取重量级锁时,其他线程会被阻塞,再次唤醒的话需要借助操作系统去完成,要从用户态转换为内核态,转换状态非常耗费资源。

      4. lock锁

        1. 实现ReentrantLock

        2. 实现ReentrantReadWriteLock读写锁

        3. 读写锁特性

          1. 非公平模式读写顺序不确定

          2. 公平模式读写顺序确定

          3. 可重入线程获取锁后还可以获取该锁

          4. 锁降级

            1. 锁升级,读锁变写锁

            2. 锁降级,写锁变读锁

          5. ReentrantReadWriteLock不支持锁升级,会产生死锁。

          6. 读锁和写锁互斥

      5. lock锁效率高于Synchronized锁

      6. synchroizes和lock锁区别

        1. synchroizes是java中的一个关键字,属于内置语言,用来解决多线程安全问题,隐式锁,遇到异常自动释放。synchrionized使用Object对象本身的wait,notify,notifyAll调度机制,而lock可以使用condition进行线程之间的调度,synchronized存在明显性能问题就是读与读之间的互斥。

        2. lock是一个接口

          1. lock等待锁过程中可以中断等待,而synchronized只能等待锁的释放,不能响应中断。

    2. 线程池

      1. 为什么使用线程池:

        1. new Thread新建对象性能差

        2. new Thread线程缺乏统一管理可能导致资源占用

        3. new Thread功能单一

      2. 通过Executors工具类新建线程池方式

        1. newCachedThreadPool:可缓存线程池,线程池长度超过需求,可灵活回收空闲线程,无可回收则新建线程。

        2. newFixedThreadPool:定长线程池,可控制线程最大并发数,超出线程可在队列等待

        3. newScheduledThreadPool:定长线程池,支持定时以及周期性任务。

        4. newSingleThreadExecutor:创建一个单线程线程池,它只会用唯一的工作线程来执行任务。

      3. Executors工具类新建线程池弊端

        1. newFixedThreadPool和newSingleThreadExector:主要问题是堆积的请求处理队列可能会耗费非常大内存,甚至oom。

        2. newCachedThreadPool和newScheddulledThreadPool:主要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM。

      4. 所以使用推荐的ThreadPoollExecutor

        1. corePoolSize:核心线程数,不能小于0

        2. maxiumumPoolSize:最大线程数,要大于核心线程数

        3. keepAliveTIme:线程池闲置时间 不小于0

        4. unit:线程时间单位

        5. workQueue:线程池中认为队列 不为空

        6. threadFactory:线程工厂 不为空

        7. 拒绝策略:不为空

          1. 丢弃任务不抛出异常

          2. 丢弃等待最久任务

          3. 丢弃任务抛出异常

          4. 主线程直接执行,不抛弃任务,也不抛出异常。

      5. 线程池创建流程

        1. 线程池内部其实是构建了一个生产者,消费者模型,将线程和任务解耦,提高线程复用率。

        2. 线程池运行主要分为两个部分,任务管理和线程管理。任务管理是生产者,任务提交后,线程池会判断该任务的后续流转

          1. 直接申请线程执行该任务

          2. 缓存到队列中去等待线程执行

          3. 拒绝该任务

          4. 线程管理部分是消费者,他们被统一维护在线程池内,根据任务请求进行线程分配,线程执行完之后会继续执行新的任务,线程获取不到任务时,就会被回收。

        3. 设置线程池参数

          1. cpu密集型任务:该任务需要大量运算,没有阻塞,cpu全速运行,该任务只有在多核cpu上才能实现加速。

          2. io密集型任务:该任务需要大量io,大量的阻塞。比如,数据库插入数据时,就是将存在磁盘的数据插入到数据库中,此时数据库就是空闲时间。

          3. 针对上诉两类任务,分别设置线程池大小

            1. cpu密集型的任务,尽可能设置少线程,大小参照cpu核数。

            2. io密集型,应该多点线程,因为过多的线程使cpu在io阻塞时还会调用其他线程。这时线程池大小应该设置为当前机器cpu核数的两倍基础上多些。

        4. 实战

          1. 不看后悔的项目中线程池实际应用_线程池实际项目中应用_木子雷的博客-CSDN博客

        1.  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值