Spring的IoC容器之 ApplicationContext
《Spring揭秘》读书笔记
Spring的IoC容器之 BeanFactory
《Spring揭秘》读书笔记
JVM 垃圾回收
在堆里面存放着Java世界中几乎所有的对象实例,垃圾收集器在对堆进行回收前,第一件事情就是要确定这些对象之中哪些还“存活”着,哪些已经“死去”。
Java对象内存结构
对象的创建当 new 一个新的对象时,首先检查这个对象所属的类是否已经被加载、解析和初始化。如果没有那必须先执行类加载过程。接下来虚拟机将为新生对象分配内存,为对象分配空间的任务实际上便等同于把一块确定大小的内存块(类加载完毕后对象大小也被确定)从 Java 堆中划分出来。
对象内存空间分配分配空间有两种方式:指针碰撞和空闲列表。
指针碰撞:假设Java堆中内存是绝对规整的,所有被使用过的内存都被放在一边,空闲的内存被放在另一边,中间放着一个指针作为分界点的指示器,那所分配内存就仅仅是把那个指针向空闲空间方向挪动一段与对象大小相等的距离,这种分配方式称为“指针碰撞”(Bump The Pointer)。
空闲列表:但如果Java堆中的内存并不是规整的,已被使用的内存和空闲的内存相互交错在一起,那就没有办法简单地进行指针碰撞了,虚拟机就必须维护一个列表,记录上哪些内存块是可用的,在分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录,这种分配方式称为“空闲列表”(Free List)。
选择哪种分配方式由Java堆是否规整决定,而Java堆是否规整又由所采用的垃圾收 ...
线程基础(下)
承接上文,本文将介绍:线程的生命周期、如何捕获线程异常、以及可以让并行变串行的join()方法
线程生命周期
在这张图片中,包含了线程的所有状态以及每种状态之间的互相转换过程。其中箭头的指向是固定的,单箭头的指向则表明了两个线程的状态是不可逆的,一旦从一端到另一端之后就无法再回去原来的状态。
NEW、RUNNABLE、TERMINATED
新创建(NEW)线程一经创建,也就是去 new 了一个 Thread 类之后,未调用 start 方法之前,这时的线程就是「新创建」的状态
可运行(RUNNABLE)有些地方可能将「可运行」状态称之为「就绪」状态,这两者其实都是「RUNNABLE」状态
调用 start 方法开始,直到 run 方法中的代码执行完毕之前,如果没有其他的操作使得线程状态跑到上图右边的三种状态中去的话,线程将会一直处于「可运行」状态。
注意:线程并没有一个「运行」的状态,就算是正在执行 run() 方法的线程状态也是「可运行」状态。
已终止(TERMINATED)当 run 方法的代码执行完毕或者是抛出未处理的异常的时,线程就会处于「已终止」状态中。这个状态也 ...
synchronized 锁的方法,不急就再等等
在 synchronized 的使用中,如果继续往下执行代码的条件不被满足的话可以先释放当前持有的锁对象再等等,等到执行条件被满足后再接着往下执行。
就像平时去医院排队看病一样,轮到我们了在医生问诊的过程中,可能会先让你先去做某一项检查,等检查完拿到检查结果之后再重新排队等待医生叫号。排队看病的例子中,当我们在被医生问诊的时候,我们就独占了医生这把锁;需要做某一项检查的时候就是往下继续问诊的流程继续不了了;拿到检查结果后我们也要重新排队等待医生继续问诊。
在程序的世界里,我们使用 synchronized 锁来保护医生对象,一个医生同一时刻只能给一个病人问诊;调用 wait() 方法先去做检查;当问诊结束了调用 notify() 方法去通知下一位病人问诊。
wait() 方法介绍wait() 方法的使用:
123456789101112131415synchronized (object) { object.wait();}synchronized (Object.class) { Object.class.wait();}pub ...
synchronized 是怎样保证线程安全的
一句话说明 synchronized 关键字的作用:
保证在 「同一时刻」 最多只有 「一个」 线程执行该段代码,以达到保证 「并发安全」 的效果
上一篇文章讲述的线程不安全的例子,现在就给出解决办法:synchronized。
synchronized 关键字的使用方式为了阅读方便,把前一篇文章中的示例代码再放到这里:
12345678910111213141516171819202122public class Task implements Runnable { // 共享变量 static int num = 0; @Override public void run() { // 对共享变量的修改 for (int i = 0; i < 100_000; i++) { num++; } } public static void main(String[] args) { final Thread ...
Java运行时数据区域
Java 虚拟机在执行 Java 程序的过程中会把它所管理的内存划分为若干个不同的数据区域。分别有:
程序计数器(Program Counter Register)
虚拟机栈(VM Stack)
本地方法栈(Native Method Stack)
方法区(Method Area)
堆(Heap)
其中,程序计数器、虚拟机栈、本地方法栈这三个区域是线程隔离的区域,每个线程都有自己的程序计数器、虚拟机栈、本地方法栈,所以这三个区域内的变量是线程安全的。我们称这类内存区域为“线程私有”的内存,他们的生命周期与线程相同。
方法区和堆则是所有线程共享的区域,存放在这里面的数据需要注意线程安全问题。
程序计数器程序计数器(Program Counter Register)是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。它是程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。
由于Java虚拟机的多线程是通过线程轮流切换、分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)都只会执行一 ...