java多线程基础

java线程的生命周期:六种(NEW,RUNNABLE,WATIING,TIME_WAITING,BLOCKED,TERMINATED)

说一说进程有多少种状态,如何转换?

新建->就绪<->运行->阻塞or终止(阻塞到就绪,然后继续运行直到终止)

上下文切换的原理
当线程从CPU占有状态退出时会发生线程切换,意味着需要保存当前线程的上下文,待线程下次运行时恢复现场,而且要加载下一个占用CPU的线程上下文,这就是上下文切换。

进程间,线程间的通信方式
进程间:管道、消息队列,信号量,共享存储
线程之间的通信方式:wait和notify等方法

死锁以及如何避免死锁

 产生死锁的原因可以归结为两点:1.竞争资源2.进程的推进顺序非法。当多个进程占有自己的资源并请求对方的资源时,会导致每个进程都无法先前推进,这就是死锁。

  • 互斥条件:该资源任意一个时刻只由一个线程占用。
  • 请求与保持条件:进程占有一些资源的同时,请求其他资源
  • 不剥夺条件:线程在使用完资源之前,资源是不能被强制夺走的
  • 循环等待条件:存在一种进程资源的循环等待链

死锁的处理策略可以分为死锁预防、死锁避免、死锁检测和解除

死锁预防:破坏死锁的必要条件,比如一次申请所有资源、进程请求资源失败后释放所占用的资源、顺序分配资源等

死锁避免:在资源的动态分配过程中,使用银行家算法防止系统进入不安全序列

(银行家算法在每次资源分配前,首先检查系统是否有足够的资源满足请求,若有则进行试分配,并对分配后的新状态进行安全性检查,维护了Allocation、Max、Need等n*m的矩阵与request,available,work等向量)

死锁检测与解除:通过资源分配图是否可以完全简化来判断,资源剥夺法、撤销进程法、进程回退法等解除死锁

线程的创建方式有哪些?

实现Runnable接口(没有返回值,不会抛出异常),实现run()方法

实现Callable接口(可以有返回值抛出异常)返回值封装在FutureTask类中

继承Thread类,thread.start()执行线程

java的锁机制

两种锁机制,第一种是JVM实现的synchronized,另一个是JDK实现的ReentrantLOCK

  • synchronized对代码块和方法修饰,产生对象锁,不同对象进行同步
  • 对静态方法或类修饰,产生类锁,不同实例化对象也会同步
  • ReentrantLock锁,实例化Lock lock =new ReentrantLock,显式上锁解锁

synchronized与volatile区别

JMM(java内存模型)中,线程可以将变量保存在本地内存中,多线程下可能造成本地变量与主存变量不一致。volatile关键字保证变量的可见性,防止JVM的指令重排,但不能保证原子性。

synchronized修饰代码块或方法,生成类锁或者对象锁

线程协作机制

join()方法挂起当前线程,直到目标线程终结

wait()进入等待状态挂起当前线程,等待其他线程调用notify或者notifyall方法唤醒线程,会释放锁

condition通过await和signal实现相似功能

Thread的方法

  • sleep()方法没有释放锁,而wait()方法释放了锁
  • sleep()用于线程暂停,时间到达后会自动苏醒
  • wait()用于线程间的交互通信,调用后需要其他线程调用同一个对象上的notify()或者notifyAll()来进行通知唤醒
  • notify()与notifyall(),join()等同步方法
  • start()运行方法

start()方法与run()方法的区别

新建一个线程,调用start方法会使线程进入就绪状态,分配到时间片可以直接运行,start()会自动执行run()方法的内容,进行多线程工作

单独执行run()方法只是在main()线程下的普通方法,而不是多线程方法

线程池

线程池的工作单元(实现Runnable接口和Callable接口,继承Thread),执行机制由Executor框架提供

池化技术通过重复利用已创建的线程降低线程创建和销毁造成的消耗

ThreadPoolExecutor的七大核心参数(核心线程数、最大线程数、阻塞队列,时间,线程工厂和饱和策略)

  • corePoolSize : 核心线程数定义了最小可以同时运行的线程数量。
  • maximumPoolSize : 当队列中存放的任务达到队列容量的时候,当前可以同时运行的线程数量变为最大线程数。
  • workQueue: 当新任务来的时候会先判断当前运行的线程数量是否达到核心线程数,如果达到的话,新任务就会被存放在队列中
  • 配置实践:CPU密集型设置为N+1,iO密集型设置为2*N,N为CPU核心数量

请求到达的处理顺序

  • 当前运行的线程数量是否少于核心线程数,是则创建新的工作线程来执行任务
  • 否则将任务加入阻塞队列
  • 阻塞队列已满则创建一个新的工作线程,不能超过maximunpoolSize
  • 否则会交给拒绝策略处理任务

拒绝策略有哪些

  • AbortPolicy(默认)直接抛出异常
  • CallerRunPolicy:用调用者所在的线程来执行任务
  • DiscardOldestPolicy:丢弃阻塞队列中靠前的任务执行当前任务
  • DiscardPolicy:直接丢弃任务

无界队列的坏处(也就是Executors工具提供的三种创建方式(FIXED,SINGLE,CACNHED)的弊端)

使用无界队列,则可以无限接受任务导致饱和策略失效,可能造成资源耗尽

 

posted @ 2022-01-09 14:39  黑白灰java  阅读(84)  评论(0)    收藏  举报