并发编程的基础知识

并发编程基础

线程生命周期

  1. 初始化:创建线程对象
  2. 就绪状态: 调用start方法,等待CPU资源
  3. 运行状态: 获取cpu资源,run方法开始执行
  4. 阻塞状态: sleep,wait,yield
  5. 销毁: 释放CPU…资源

Java线程函数

Object提供了线程的等待与通知的函数

wait

线程等待,当一个线程调用一个共享变量的wait函数时,该线程会被阻塞挂起,直到发生下面几件事才会返回:

1.其他线程调用该共享变量的唤醒方法(notify,notifyAll)
2.其他线程调用了该线程的 interrupt 函数,该线程抛出InterruptedException 并返回
3.线程没有通过上面两种方式从挂起阻塞状态转变为运行状态,这是特殊情况,我们可以称为虚假唤醒

使用wait函数时需要获取调用对象的监视器锁,非则会抛出IllegalMonitorStateException异常

notify

调用notify()会随机唤醒共享变量挂起的线程,notifyAll()唤醒全部被共享变量挂起的线程

被唤醒的线程不能马上执行逻辑,还需要获取共享变量的监视器锁才可以,这中间可能需要和其他线程去争监视器锁

join

使用场景:

在项目中,需要等待某几件事情完成后才能继续往下执行,比如多个线程加载资源,需要多个资源汇总完毕再处理

通知和唤醒函数是Object的方法,join函数由Thread提供new Thread().join(),join无参数且无返回值

sleep

调用sleep方法的线程会暂时让出指定时间的执行权限,这段期间不参与CPU的调度,但不会释放监控资源,比如锁是不会释放的,睡眠到时间后会返回,争取CPU资源

yield

当线程内部调用 Thread.yield()时,线程会让出CPU资源,进入就绪状态,去争夺CPU资源,这意味yield不一定能让调用线程进入阻塞状态

线程中断

函数由Thread提供,通过设置线程的中断标志,不代表立马结束该线程的执行,而是被中断的线程根据中断标志自行处理

// 中断线程,注意,当遇到线程正被wait,join,sleep函数阻塞挂起时,只会抛出中断异常,而返回继续执行子线程逻辑
void interrupted()

//检测当前线程是否被中断 是 true 否则false
boolean isInterrupted()

// 中断线程 成功 true 否则 false;静态函数,如果当前线程被中断,那么会清除线程的中断标志
static boolean interrupted();

好了基本该了解的线程基础函数就到这里

对象的锁和监听器

每一个Object类及其子类的实例都拥有一个锁(在object header中)。其中,基础类型int,float等不是引用类型,但是基础类型可以通过其包装类来作为锁。单独的成员变量是不能被标明为同步(synchronized)的。锁只能用在使用了这些变量的方法上。成员变量可以被声明为volatile,这种方式会影响该变量的原子性,可见性以及排序性。

类似的,持有标量变量元素的数组对象拥有锁,但是其中的标量元素却不拥有锁。(也就是说,没有办法将数组成员声明为volatile类型的)。如果锁住了一个数组并不代表其数组成员都可以被原子的锁定。也没有能在一个原子操作中锁住多个对象的方法。

每一个对象拥有一个由这些方法(wait,notify,notifyAll,Thread,interrupt)管理的一个等待集合。拥有锁和等待集合的实体通常被称为监视器,任何一个对象都可以作为一个监视器。

对象的等待集合是由Java虚拟机来管理的。每个等待集合上都持有在当前对象上等待但尚未被唤醒或是释放的阻塞线程

因为与等待集合交互的方法(wait,notify,notifyAll)只在拥有目标对象的锁的情况下才被调用,因此无法在编译阶段验证其正确性,但在运行阶段错误的操作会导致抛出IllegalMonitorStateException异常。

线程上下文切换

线程个数一般大于CPU个数,每个CPU同一时刻只能被一个线程使用,而在单CPU中,为了让用户感觉多个线程同时执行,CPU资源分配采用了时间片段轮转的策略
每个线程都会有一个时间片,在时间片内占用CPU资源,使用完后会从运行状态转变为就绪状态,让出CPU资源,这就是上下文切换

如果上个线程在时间片内没有执行完逻辑,那么下次占有CPU资源时,不可能重新执行,所以切换线程上下文时需要保留线程的执行现场,下次才能回到之前执行的位置

线程上下文切换的时机:

  • 当线程的CPU时间片用完处于就绪状态
  • 当前线程被其他线程中断

可以理解为当从运行状态转为就绪状态,让出了资源,即是上下文切换

线程死锁

两个线程执行过程中,因争夺资源(最常见的是锁)而互相等待的线程,在没有外力干扰的情况下,这些线程会一直僵持

died lock

产生死锁的四个条件

  • 互斥条件
    资源只能被一个线程占用,其他请求资源的线程只能等待

  • 请求并持有资源
    指一个线程已经持有资源了,但还需要请求新的资源,而被请求的资源已经被占用,那么该线程就被阻塞了,但又不会是否已经持有的资源

  • 不可剥夺条件
    线程持有的资源在用完之前都不会释放,和被其他线程抢占

  • 环路等待
    线程相互等待,形成一个闭环

守护进程与用户线程

Java的线程有两种,分别是daemon线程(守护进程)和user线程(用户线程)

执行main函数的就是一个user thread,执行gc的就是 daemon thread

JAVA设置线程为守护进程

Thread.setDaemon(true)

参考资料

Java并发结构

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值