进程和线程
进程:是操作系统进行资源分配的基本单位。
线程:是程序执行的最小单位。
一个进程可以拥有多个线程,这些线程共享进程的内存空间。
JVM内存模型
栈:存放局部变量,包括基本数据类型和对象的引用。
堆:存放对象实例、数组。
方法区:类信息、静态变量、常量。
每个线程都有独立的栈,所有线程共享堆和方法区。
栈帧:每次方法的调用,都会在栈上维护一个独立的栈帧。
字符串常量池在堆中。
GC垃圾回收机制
堆分为:新生代、老生代 (比例 新:老 = 1:2)
新生代分为:Eden区和两个幸存区
比例 Eden:S1:S2 = 8:1:1
采用分代回收算法
新建对象时放在Eden区,垃圾回收时清理Eden区把留下的放在其中一个幸存区S1中,下次垃圾回收是清理Eden区和S1,留下的放在S2,交替清理S1和S2,每次清理时都对幸存的进行计数,6次回收都留下的放到老生代区。
新生代中采用的是复制算法,老生代中采用的是标记整理算法。
标记整理算法:将存活的对象移向内存的一端,然后清除剩余的空间。
volatile和synchronized
线程安全的两个方面:执行控制和内存可见。
执行控制:控制代码执行顺序及是否可以并发执行。
内存可见:控制线程执行结果在内存中对其它线程的可见性。线程在具体执行时,会先拷贝主存数据到线程本地(CPU缓存),操作完成后再把结果从线程本地刷到主存。
synchronized用来修饰一个方法或者一个代码块,能够保证在同一时刻最多只有一个线程执行该段代码,还会把操作结果都会直接刷到主存中,从而保证了操作的内存可见性。
volatile修饰的变量读写都会直接刷到主存,即保证了变量的可见性。
区别:
- volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的
- volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性。
- volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。
sleep()和wait()
sleep()让调用的线程进入指定时间的睡眠状态。
线程执行wait()方法,它就进入到一个和该对象相关的等待池中,同时释放对象锁,并让出CPU资源,待指定时间结束后返还得到对象锁。
区别:
- 属于不同的两个类,sleep()方法是线程类(Thread)的静态方法,wait()方法是Object类里的方法。
- sleep()方法不会释放锁,wait()方法释放对象锁。
- sleep()方法可以在任何地方使用,wait()方法则只能在同步方法或同步块中使用。
- sleep()使线程进入阻塞状态(线程睡眠),wait()方法使线程进入等待队列(线程挂起),也就是阻塞类别不同。
网友评论