TCP的滑动窗口
知乎视频解释:网络传输原理:什么是TCP滑动窗口
单例模式(懒汉饿汉)
传统课本上单例模式分两种,一种饿汉式,一种懒汉式。对应的代码如下:
详解双重饿汉模式和懒汉模式:单例模式中的懒汉模式,饿汉模式 , 双检锁/双重校验锁
饿汉式
public class Singleton {
private static Singleton instance = new Singleton(); // 在类加载的时候就会实例化
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
饿汉式在类加载时就实例化对了,使用时直接调用 getInstance() 方法
。这个模式为线程安全的,在多线程并发模式下不会重复实例化对象。
优点: 效率高 缺点: 对象实例化过早,浪费内存资源
懒汉模式
public class Singleton {
private static Singleton singleton;
private Singleton() {
}
public static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
这种模式没有在加载时实例化对象,而是在调用getInstance() 方法时实例化对象
,使用懒汉式是为了避免过早的实例化,减少内存资源浪费。
优点:第一次调用才初始化,避免内存浪费。 缺点: 只适合单线程,线程不安全
参考:用单例模式来讲讲线程安全
双检锁/双重校验锁(DCL,即 double-checked locking)
参考:双检锁/双重校验锁(DCL,即 double-checked locking)详细解析
public class Singleton {
private volatile static Singleton instance;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();//erro
}
}
}
return instance;
}
public static void main(String[] args) {
Singleton.getInstance();
}
}
为什么要双重校验
public static Singleton getInstance() {
if (instance == null) {//线程1,2同时到达,均通过(instance == null)判断。
// 线程1进入下面的同步块,线程2被阻塞
synchronized (Singleton.class) {
if (instance == null) {//线程1执行发现instance为null,初始化实例后,释放锁。
// 线程2进入同步块,此次instance已经被初始化。无法通过if条件,避免多次重复初始化。
instance = new Singleton();
}
}
}
return instance;
}
执行双重检测是因为,如果多个线程通过了第一次检测,此时因为synchronized,其中一个线程会首先通过了第二次检测并实例化了对象,剩余的线程不会再重复实例化对象。这样,除了初始化的时候会加锁,后续的调用都是直接返回,解决了多余的性能消耗。
为什么使用volatile加双锁
if (instance == null) {
instance = new Singleton();//erro
}
如果不使用volatile关键字,隐患来自于上述代码中注释了 erro 的一行,这行代码大致有以下三个步骤:
- 在堆中开辟对象所需空间,分配地址
- 根据类加载的初始化顺序进行初始化
- 将内存地址返回给栈中的引用变量
由于 Java 内存模型允许“无序写入”,有些编译器因为性能原因,可能会把上述步骤中的 2 和 3 进行重排序,顺序就成了
- 在堆中开辟对象所需空间,分配地址
- 将内存地址返回给栈中的引用变量(此时变量已不在为null,但是变量却并没有初始化完成)
- 根据类加载的初始化顺序进行初始化
参考:双重校验锁实现单例模式