并发编程--多线程基础知识总结

什么是进程跟线程

  • 进程 进程是程序的一次执行过程,是系统运行程序的基本单位,启动一个main函数其实就是启动了一个jvm进程,main函数所在的线程就是这个进程中的一个线程,也就是主线程.
  • 线程,线程是一个比进程更小的执行单位,一个进程再起执行的过程中可以产生多个线程,系统在各个线程之间切换工作的代价会小的多
  • 线程跟进程之间有什么区别
  • 一个进程可以有多个线程,多个线程共享进程的堆和方法区,但是每个线程拥有自己的程序计数器,虚拟机栈,和本地方法栈
  • 线程是进程划分成的更小的运行单元,线程和进程最大的区别在于基本上各个进程之间是相互独立的,但是线程之间极有可能会相互影响,线程执行开销小,但是不利于资源的维护,二进程正相反

并发与并行的区别

  • 并发 俩个即俩个以上的作业在同一时间段内执行
  • 并行 俩个或者俩个以上的作业在同一时刻执行
  • 最关键的是 是否同时执行

同步与异步的区别

  • 同步 发出一个调用之后在没有得到结果之前该调用就不可以返回,一直等待
  • 异步 调用在发出之后不用等待返回结果,该调用直接返回

为什么使用多线程

  • 现在的系统东欧东就是要求百百万级别升至千万级别的并发量,而多线程并发编程正是开发高并发系统的基础,利用好多线程机制可以大大提高系统整体的并发能以及性能
  • 线程可以比作轻量级的进程的程序执行的最小单位,线程之间的切换和调度成本远远小于进程,另外,多核cpu时代意味着多个线程可以同时运行,着减少了线程上下文切换的开销

使用多线程可能带来的问题

并发编程的目的就是为了能提高程序的执行效率提高程序运行速度.但是并发编程并不是中能提高程序运行速度的,并且并发编程可能会遇到很多问题,而且鬓发编程可能会遇到很多问题比如内存泄露,死锁,不安全等等

  • 内存泄漏 程序中已经动态分配的堆内存有某种原因,程序未释放或者无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果

线程的生命周期和状态

  • new状态 线程被创建出来但是没有被调用Start()
  • bunnable 运行状态 线程被调用了Start()等待运行的状态
  • blocked 阻塞状态 需要等待锁的释放
  • waiting 等待状态 表示该线程需要等待其他线程做出一些特定动作(通知或者中断)
  • time_waiting 超时等待状态 可以在指定的时间后执行返回而不是像waiting那样一直等待
  • terminated 终止状态,表示该线程已经运行完毕
  • 创建—就绪—运行----阻塞----死亡

什么是上下文切换

  • 上下文是指线程在执行过程中还有知己的运行条件和状态(也称为上下文),比如上文锁说到的程序计数器,栈信息等,当初出现如下情况的时候线程会从占用CPU状态分钟退出
    • 主动让出cpu,比如调用了sleep(),wait()等
    • 时间篇用完,因为操作系统要防止一个线程或者进程长时间占用cpu到值其他线程或者进程饿死
    • 调用了阻塞类型的系统中断你如请求io,线程被阻塞
    • 被终止或者运行结束
  • 其中前三种都会发生线程的切换,这时候就需要保存线程的上下文状态,等待线程下次占用cpu的时候恢复现场,并加载下一个将要占用cpu的线程上下文,这就是所谓的上下文切换
  • 上下文是现在操作系统的基本功能,因为其每次需要保存信息恢复信息,这将会占用cpu,内存等系统资源进行处理,这样就会意味着,效率会有一定的损失如果频繁切换上下文就会导致整体效率的地下

什么是线程死锁

  • 线程死锁描述的一种情况,多个线程同时被阻塞,它们中的一个或者多个都在等待摸个资源被释放,由于线程被无限期的阻塞,因此程序不可能正常终止,
  • 线程死锁产生的四个必要条件
    • 互斥资源该资源任意一个时刻只由一个线程占用
    • 请求与保持条件,一个线程因请求 自哦远而阻塞时,对已经获得的资源保持不放,
    • 不剥夺条件,线程已获得的资源在未使用之前不能被其他线程强行剥夺只有自己使用完毕后才释放资源
    • 循环等待条件,若干线程之间形成一种头尾详解的循环等待资源关系
  • 如何预防线程死锁
    • 破坏请求与保持条件,一次申请所有的资源
    • 破坏不剥夺条件 占有部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源
    • 破坏循环等待条件,按照顺序来申请资源来预防,按照某一顺序申请资源,释放资源则反序释放,破坏循环等待条件
  • 如何避免死锁
    • 避免死锁就是在资源分配是借助算法比如银行家算法对资源分配进行计算评估,使其进入安全状态

Sleep()方法与wait方法()方法对比

  • 共同点,两者都可以暂停线程的执行
  • 区别
    • sleep()方法没有释放锁,但是wait()方法释放了锁
    • wait()通常用来线程之间的交互/通信,sleep通常用于展厅执行
    • wait()方法被停用了之后,线程不会自动苏醒,需要别的线程调用同一个对象上的notify()方法或者notifyfyall(),sleep()方法执行完成后线程会自动苏醒,或者可以使用wait(long timeout)超市后线程会自动苏醒
    • sleep()是thread类的静态本地方法 wait()则是object类的本地方法,为什么这样设计,

可以直接调用thread类的run方法吗

  • 不可以因为new了一个thread,线程进入了新建状态,调用start()方法后进入了一个就绪状态,当分配到时间片就可以开始运行了,这时候会自动执行run方法,但是直接运行run方法就会吧run方法当做一个main线程下的普通方法去执行,并不会再摸个线程中执行他,总结: 调用 start() 方法方可启动线程并使线程进入就绪状态,直接执行 run() 方法的话不会以多线程的方式执行。

共享变量可见性、原子性 关键字volatile

  • 共享变量 一个变量再俩个线程的工作内存都存在副本(从主内存中复制过来的),这个变量就是共享变量,
  • 可见性,多线程下一个线程修改共享变量,能够即使被其他线程所感知
  • 原子性
  • 当一个线程访问一个变量时,该操作不会被中断,保证数据操作是以原子方式进行的;原子性是拒绝多线程操作的,不论是多核还是单核,具有原子性的量,同一时刻只能有一个线程来对它进行操作。简而言之,在整个操作过程中不会被线程调度器中断的操作,都可认为是原子性
  • 共享变量的可见性实现原理:线程1修改共享变量后,立刻更新主内存,然后线程2立刻从主内存把值更新到线程2的工作内存
  • synchronized和volatile都能保证变量的原子性,但还是有区别的
    • synchronized需要加锁,volatile不需要加锁,volatile不会阻塞线程执行效率高
    • synchronized保证可见性和原子操作,volatile保证可见性和不保证原子操作
  • 使用volatile时应该注意,不要使用i++等操作,因为i++不是一个原子性的操作,有因为volatile无法保证操作的原子性,所以这里就会出现结果与预期解雇哦不一致的情况,
  • volatile还可以禁止指令的重排序,具体内容等待后续补充

Synchronized关键字

  • 主要解决的是多个线程之间访问资源的同步性,可以保证被它修饰的方法或者代码块在任意时刻只能有一个线程执行。]\
  • Synchronized的与用法
    • 修饰实例方法 (锁当前对象实例)
    • 修饰静态方法 (锁当前类)
    • 修饰代码块 (锁指定对象/类)可以把一个对象当做一个锁来使用,或者直接锁指定的类但是注意尽量不要给String类型上锁
    • 构造方法不要使用synchronized关键字修饰,因为构造方法本身就是线程安全的
  • synchronized关键字在1.6进行过一次升级,降低了开锁和关锁的花费,

synchronized 和 ReentrantLock 的区别

  • 俩者都是可重入锁
  • synchronized 依赖于 JVM 而 ReentrantLock 依赖于 API
  • ReentrantLock 比 synchronized 增加了一些高级功能

ThreadLocal

  • 让每一个线程都有自己专属的本地变量,即存放在线程的工作内存中的本地变量,其他线程不能访问,
  • 使用场景,存储用户的session,Spring解决线程安全问题
  • 使用threadLocal可能会导致内存泄漏问题,因为ThreadLocalMap 中使用的 key 为 ThreadLocal 的弱引用,而 value 是强引用,在没有外部强引用的情况下,在垃圾回收的时候后key会被清理掉,但是value不会被清理掉,就会除先key为null的entry,并且无法为回收,就会出现内存泄漏的问题
  • 如何解决,在使用完threadlocal方法后调用一次remove方法即可 ,等待后续补充,好像现在不用掉用,调用set与get方法本身就可以

创建线程的三种方式

  • 继承thread类并重写run方法
  • 实现runnable接口并实现run方法
  • 实现callable接口重写call方法 还需要擦混构建一个执行服务
  • 前俩种方法在实现run方法后都需要在创建一个thread对象并传入该类的对象,后调用thread对象的start()方法来启动线程

实现 Runnable 接口和 Callable 接口的区别

  • Callable 接口可以返回结果和抛出异常
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值