进程和线程的区别和联系,为什么程序需要锁和同步

在系统中的作用不同:

进程:系统分配资源的基本单位,用来承载需要使用的系统资源。
线程:CPU调度的基本单位,用来调用计算资源。

程序运行时他们之间的关系

应用的运行需要系统资源支持,因此需要创建进程,应用才能运作。而对资源做处理以得到想要的结果,就需要调用CPU进行运算。调用CPU资源就需要创建线程。

在创建应用时,就会创建一个进程,一个进程默认创建一个主线程。进程可以使用系统资源获得相应的输入,线程使用进程的资源进行相应的计算产出相应的结果,同一进程的多个线程可以共享进程中的全局变量,当两个或以上的线程同时使用同一全局变量时就会产生冲突,就需要对全局变量进行同步,同步就需要锁。

JVM与线程

线程栈(Thread Stack)存放着调用栈和本地变量;对象、静态变量、成员变量则存放在JVM堆(Heap)中,对象和其成员变量可以被线程访问,每个线程都会拥有成员变量的私有拷贝,线程结束,线程栈释放栈内存,而堆伴随着应用的生命周期。

java开启新进程,分配一个存放资源的内存堆,堆中的资源是可以全局共用的,GC是在堆中进行的;线程都拥有自己的线程栈,线程栈为线程私有。

栈内存满了会报StackOVerflowError,堆内存满了报OutOfMemoryError。

锁相关

volatile:

被此关键字的变量,不会放到缓存中,需要在主存中读写,所以读写效率更低;它能保证下一个读操作在上一个写操作之后。
它的使用场景是一写多读,一个线程操作,多个线程读,也就是实现改变可见性。

synchronized
happens-before

如果一个操作的结果要对另一个操作可见,那么两个操作需要有happens-before关系
volatile的写happens-before于读。
lock的解锁happens-before于上锁。
synchronize是一个上锁的关键字,对静态变量上锁,锁的是类,对非静态变量锁的是对象。

双重锁:保证单例的完整性
为什么单例会有问题:创建对象有三步 1 分配对象内存 2 实例化 3 地址指向实例。
public static Singleton getSingleton() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance ;
}
当地址指向实例,实例不为空。指令重排时,对象创建可能会出现1-3-2,这种情况下,当线程A进入方法,执行到3时,对象还没有实例化,而这时线程B进入if判断,发现实例已经不为空,获得了未实例化的对象。

public class Singleton{
private volatile static Singleton instance;
public static Singleton getSingleton() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance ;
}
}
使用volatile关键字,让对象先写后读,一写多读,让操作可见,避免此问题。

public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
当前比较有效率的一种写法,用静态内部类,实现了懒加载又不会出现线程安全问题,减少了synchronized的开销。

你可能感兴趣的:(进程和线程的区别和联系,为什么程序需要锁和同步)