多线程的底层与应用场景

在说多线程之前我想问问你们是如何理解同步与异步的呢?

那么我来讲一讲同步异步的区别,首先我拿微信消息微信聊天功能来举例子!
同步: 当我们要给家人或者朋友在微信上打电话的时候,要想联系到目标对象我们得先拨打,对方还要按接听,才能达成通话的目的. 表示我在做这个任务的时候,和你建立连接对你发起任务 你也得回应给我任务. 双方同时做的事情就是在打电话. 这就是同步.

异步:  异步呢,就是我给你发条消息,比如你很讨厌我不想秒回我~~ 那就选择过段时间再回,并且我在等你回复的这段时间里可以做别的事情,比如吃个饭洗个手等等,直到你回我消息,这种任务的过程便是异步,我不用刻意在屏幕前一直等着你回,还可以用这段空余时间去做别的事情.也就是不用等待,但打电话不同  给你拨打电话的时候我必须把手机放在耳朵旁或者眼前等待你的接听,如果你有蓝牙就算了哈! 

理解同步异步有很多种方式 这只是其中一种希望可以给你提供帮助!

           进     入       正       题

↓  ↓  ↓  ↓  ↓  ↓  ↓  ↓  ↓  ↓  ↓  ↓  ↓  ↓  ↓  ↓

实现线程的方法有哪些?

1、创建thread方法

2、实现runable接口

3、实现callable接口(可以获取线程的返回结果)

runable和callable的区别

1、runable是实现run(),callable是实现call()

2、runable无返回值,callable有返回值

3、runable不抛异常,callable抛异常

如何让线程的有序执行

采用join

countdownlatch类,首先有countdown()和await(),当调用countdown()时,计数器不为0的时候就会执行await(),如果技术器为0则会执行下一个任务

采用 Executors的单线程池创建方式 Executors.newSingleThreadExecutor();

如何获取线程的返回结果?

实现callable接口,返回Futrue,通过get方法可以获取结果

线程数过多会造成什么情况

1、消耗资源

2、占用cpu

3、降低了稳定性

解决线程安全的方式有哪些?

1、加synchronized

2、加lock锁  此时采用的是Reentratlock

3、ThreadLocal

4、atomic

5、提供了一些线程安全的类比如ConcurrentHashmap

线程池的底层原理

创建线程池的时候,开始一个线程也没有,随着任务的提交创建线程,当前线程如果小于corePoolSize核心线程数创建线程, 否则(当前线程大于或等于核心线程数)放入LinkedBlockingQueue队列,如果队列没有满,继续放入,如果队列满了, 判断是否小于最大线程数,如果小于继续创建线程,否则拒绝策略(默认拒绝策略抛异常)

 拒绝策略有哪些?

1、使用线程解决

2、直接拒绝不抛异常

3、直接拒绝抛出异常(默认)

4、将最早的线程舍弃,将最新线程添加

创建线程池的方法有哪些?

1、单一线程池(始终只有一个线程)

2、定时线程池(在固定时间段内使用线程)

3、定长线程池(自定义设置线程大小  一般使用)

4、可缓存线程池(无限制的线程大小)

synchroized的底层原理

synchroized是通过监视器monitor来完成的,如果monitor被占用时会处于死锁的状态,线程需要 执行monitorenter指令去尝试获取monitor的所有权,如果monitor的进入数为0,那么进入现场进入monitor,然后将进入数设置为1,此线程为monitor的所有者,如果线程已经有monitor需要重新进入,monitor为+1,如果已经占用了monitor,则该线程进入等待的状态,直到monitor的进入数为0时,再去重新获取所有权。

ReentratLock的底层原理

ReentratLock主要是通过AQS+CAS来实现的

先通过CAS去获取锁,如果锁获取到了就将线程放入AQS队列中去,如果锁释放了,就会释放AQS队列中的首个线程,在通过CAS去尝试获取锁。

底层是基于AQS实现的,每个都有自己的内部类

lock的存储结构:一个int类型(用来存放锁的状态变更)  一个是双向链表(用于存储等待中的线程)

lock获取锁的过程:本质上是通过CAS来修改状态,如果获取到锁之后就更改锁的状态,没有获取到锁就放到等待链表中进行等待。

lock释放锁的过程:修改状态,调整链表

ThreadLocal原理

当多个线程操作同一变量且互不干扰的情况下,可以使用threadlocal来解决,它会每个线程都创建一个副本,线程内部都会创建一个变量。在底层有一个巨大的map来存放线程,如果在使用完线程之后没有释放,那么会造成一个内存溢出的情况

ThreadLocal为什么会造成内存溢出

因为底层是巨大的map,而map的key是弱引用,value是强引用,而进行回收的时候可以回收掉key,value是回收不掉的,解决方法可以直接使用自带的remove

volatile 有什么作用?

  1. 防止指令重排序,2.保证各个线程之间的可见性

AQS底层原理

AQS维护volatile下的共享资源state,如果获取到锁了那么就去改变state的状态,如果没有获取到那么就进入一个阻塞的状态,放入阻塞队列中去

原理就是:AQS实际是通过CLH队列操作的,CLH队列是一个虚拟的双向队列,通过CAS去获取,获取到修改state状态,获取不到就放入阻塞队列中去

CAS原理

CAS(compare and swap)是比较交换,是一种乐观锁, VAB V是内存预期要修改的变量,A是要修改的预期值,B是新值,如果用户要去改变内存中的值,那么A要去和V去做对比,如果相等了那么B的值就是A的值。

会出现一个ABA的问题,如果有一个人吧值从原来的1改成了2,后又吧2改成了1,那么别人认为这个操作是无用的,其实对于CAS来说是用的,就会出现ABA的问题,解决方式 每修改一次就给版本号上+标识

synchronized 和 lock 有什么区别?

synchronized 是java语言的关键字,lock是一个类,通过这个类可以实现同步访问

synchronized我们一般是锁代码块,lock一般是我们需要创建一把锁,然后执行任务。最后需要手动释放锁

synchronized的底层是基于JVM指令完成,它会在代码块中加入monitorenter和monitorexit执行,而lock是通过代码实现的,底层基于AQS实现的

synchronized 获得锁和释放锁的方式都在块结构中,是 自动释放锁,lock需要手动释放,并且必须在finally中,否则会造成死锁

  • 14
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值