java基础

垃圾回收

标记法

  1. 引用计数算法,顾名思义,就是为对象添加一个引用计数,用于记录对象被引用的情况,如果计数为 0,即表示对象可回收
  2. Java 选择的可达性分析,JVM 会把虚拟机栈和本地方法栈中正在引用的对象、静态属性引用的对象和常量,作为 GC Roots。然后跟踪引用链条,如果一个对象和 GC Roots 之间不可达,也就是不存在引用链条,那么即可认为是可回收对象

JVM提供的收集器较多,特征不一,适用于不同的业务场景:

  1. Serial收集器:串行运行;作用于新生代;复制算法;响应速度优先;适用于单CPU环境下的client模式。
  2. .ParNew收集器:并行运行;作用于新生代;复制算法;响应速度优先;多CPU环境Server模式下与CMS配合使用。
  3. Parallel Scavenge收集器:并行运行;作用于新生代;复制算法;吞吐量优先;适用于后台运算而不需要太多交互的场景。
  4. Serial Old收集器:串行运行;作用于老年代;标记-整理算法;响应速度优先;单CPU环境下的Client模式。
  5. Parallel Old收集器:并行运行;作用于老年代;标记-整理算法;吞吐量优先;适用于后台运算而不需要太多交互的场景。
  6. CMS收集器:并发运行;作用于老年代;标记-清除算法;响应速度优先;适用于互联网或B/S业务。
  7. G1收集器:并发运行;可作用于新生代或老年代;标记-整理算法+复制算法;响应速度优先;面向服务端应用。

回收算法

  1. 复制(Copying)算法,我前面讲到的新生代 GC,基本都是基于复制算法,过程就如专栏上一讲所介绍的,将活着的对象复制到 to 区域,拷贝过程中将对象顺序放置,就可以避免内存碎片化。这么做的代价是,既然要进行复制,既要提前预留内存空间,有一定的浪费;
  2. 标记 - 清除(Mark-Sweep)算法,首先进行标记工作,标识出所有要回收的对象,然后进行清除。这么做除了标记、清除过程效率有限,另外就是不可避免的出现碎片化问题,这就导致其不适合特别大的堆;否则,一旦出现 Full GC,暂停时间可能根本无法接受。
  3. 标记 - 整理(Mark-Compact),类似于标记 - 清除,但为避免内存碎片化,它会在清理过程中将对象移动,以确保移动后的对象占用连续的内存空间。

多线程安全:也就是保证多线程环境下共享的、可修改的状态的正确性,这里的状态反映在程序中其实可以看作是数据。

线程安全需要保证几个基本特性:

  1. 原子性,简单说就是相关操作不会中途被其他线程干扰,一般通过同步机制实现。
  2. 可见性,是一个线程修改了某个共享变量,其状态能够立即被其他线程知晓,通常被解释为将线程本地状态反映到主内存上,volatile 就是负责保证可见性的。
  3. 有序性,是保证线程内串行语义,避免指令重排等。

一直在研究JUC方面的。所有的Lock都是基于AQS来实现了。AQS和Condition各自维护了不同的队列,在使用lock和condition的时候,其实就是两个队列的互相移动。如果我们想自定义一个同步器,可以实现AQS。它提供了获取共享锁和互斥锁的方式,都是基于对state操作而言的。ReentranLock这个是可重入的。其实要弄明白它为啥可重入的呢,咋实现的呢。其实它内部自定义了同步器Sync,这个又实现了AQS,同时又实现了AOS,而后者就提供了一种互斥锁持有的方式。其实就是每次获取锁的时候,看下当前维护的那个线程和当前请求的线程是否一样,一样就可重入了。

synchronized:monitorenter/monitorexit 指令实现的 使用方便,可重入,非公平,不可中断,由内核实现线程的阻塞睡眠,唤醒,线程阻塞会释放cpu,会有两次上下文切换消耗,通过wait、notify、notifyAll实现线程阻塞休眠(释放cpu)与唤醒

实现lock的锁

比如ReentrantLock:需要手动加解锁(和try finally配合),可重入,可以实现更精细的同步策略,

支持非阻塞,超时的加锁,可中断(响应中断请求),可排队的公平加锁

通过conditon条件变量的signal 和 await 方法,实现线程的阻塞休眠(释放cpu)与唤醒

还支持独写锁实现reentrentreadwritelock(读多写少场景)

互斥锁与自旋锁:休眠还是“忙等待”?

互斥锁:内核将线程休眠, 释放cpu,锁释放时,内核将休眠的线程唤醒,这里涉及两次上下文切换,如果不确定业务执行多久,或者执行很长时间, 一般使用互斥锁

自旋锁:自旋锁比互斥锁快得多,因为它通过 CPU 提供的 CAS 函数(全称 Compare And Swap),在用户态代码中完成加锁与解锁操作。减少了上下文切换(几十纳秒/几毫秒),不释放cpu,本地不断尝试加锁,如果业务执行时间很短(可能比切换上下文耗时还短),使用自选锁消耗可能比

加锁流程包括 2 个步骤:第 1 步查看锁的状态,如果锁是空闲的,第 2 步将锁设置为当前线程持有。CAS 函数把这 2 个步骤合并为一条硬件级指令

读写锁:读多写少,由读锁和写锁两部分构成,互斥锁和自选锁都是较为基础地两个锁。独写锁可以由互斥锁(阻塞)或者自旋锁(非阻塞,支持超时)支持

乐观锁:虽然去除了锁操作,但是一旦发生冲突,重试的成本非常高。所以,只有在冲突概率非常低,且加锁成本较高时,才考虑使用乐观锁。最后的验证步骤是通过 CPU 提供的 CAS 操作完成的

synchronized升级过程  无锁,偏向锁、轻量级锁和重量级锁,

存在对象头中markword锁标志位中,

对象头主要有两部分(数组对象有三组分)组成。 Markword, Klass 指针(数组对象的话,还有一个 length)。

markword主要存储对象运行时的一部分数据。主要内容有 hashcode,GC 分代年龄,锁状态标志位,线程锁标记,偏向线程ID,偏向时间戳等

线程状态转换

关于线程生命周期的不同状态,在 Java 5 以后,线程状态被明确定义在其公共内部枚举类型 java.lang.Thread.State 中,分别是:

  1. 新建(NEW),表示线程被创建出来还没真正启动的状态,可以认为它是个 Java 内部状态。
  2. 就绪(RUNNABLE),表示该线程已经在 JVM 中执行,当然由于执行需要计算资源,它可能是正在运行,也可能还在等待系统分配给它 CPU 片段,在就绪队列里面排队。在其他一些分析中,会额外区分一种状态 RUNNING,但是从 Java API 的角度,并不能表示出来。
  3. 阻塞(BLOCKED),这个状态和我们前面两讲介绍的同步非常相关,阻塞表示线程在等待 Monitor lock。比如,线程试图通过 synchronized 去获取某个锁,但是其他线程已经独占了,那么当前线程就会处于阻塞状态。
  4. 等待(WAITING),表示正在等待其他线程采取某些操作。一个常见的场景是类似生产者消费者模式,发现任务条件尚未满足,就让当前消费者线程等待(wait),另外的生产者线程去准备任务数据,然后通过类似 notify 等动作,通知消费线程可以继续工作了。Thread.join() 也会令线程进入等待状态。
  5. 计时等待(TIMED_WAIT),其进入条件和等待状态类似,但是调用的是存在超时条件的方法,比如 wait 或 join 等方法的指定超时版本,如下面示例:public final native void wait(long timeout) throws InterruptedException;
  6. 终止(TERMINATED),不管是意外退出还是正常执行结束,线程已经完成使命,终止运行,也有人把这个状态叫作死亡。在第二次调用 start() 方法的时候,线程可能处于终止或者其他(非 NEW)状态,但是不论如何,都是不可以再次启动的。

线程自身的方法,除了 start,还有多个 join 方法,等待线程结束;yield 是告诉调度器,主动让出 CPU;另外,就是一些已经被标记为过时的 resume、stop、suspend 之类,据我所知,在 JDK 最新版本中,destory/stop 方法将被直接移除。

线程方法:start (t线程进入runnable状态),join(内部调用wait, 调用t.join的线程阻塞,进入waiting状态),yield(主动让出cpu)

基类 Object: wait/notify/notifyAll 方法。如果我们持有某个对象的 Monitor 锁,调用 wait 会让当前线程处于等待状态,直到其他线程 notify 或者 notifyAll。所以,本质上是提供了 Monitor 的获取和释放的能力,是基本的线程间通信方式。并发类库中的工具,比如 CountDownLatch.await() 会让当前线程进入等待状态,直到 latch 被基数为 0,这可以看作是线程间通信的 Signal。wait释放锁,sleep不释放锁

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值