单例模式能保证某个类在程序中只存在唯一一份实例, 而不会创建出多个实例,并提供一个全局访问点。
饿汉模式
类加载的同时,创建实例。
class Singleton {
private static final Singleton instance = new Singleton();
//将构造方法设为私有,以防止外部通过new关键字创建新的实例。
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
- 上述代码定义了一个名为Singleton的类。
- 在类中定义了一个私有的静态常量instance,它是Singleton类的一个唯一实例。
- 提供了一个公共的静态方法getInstance(),用于获取Singleton类的唯一实例。
懒汉模式
类加载的时候不创建实例,第一次使用的时候才进行创建。
单线程版
class Singleton {
private static Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
多线程版
上述单线程代码在多线程中就会出现错误,多个线程同时调用getInstance()
方法时,就可能导致创建出多个实例是不安全的。这里我们只需要在getInstance()
方法中添加synchronized
关键字就可解决。
class Singleton {
private static Singleton instance = null;
private Singleton() {
}
public synchronized static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
双重检查锁定
class Singleton {
//volatile关键字保证了instance变量在多线程环境下的可见性。
private static volatile Singleton instance = null;
private Singleton() {}
public synchronized static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class){
if (instance == null ){
instance = new Singleton();
}
}
}
return instance;
}
}
双重检查可以这样进行理解:
第一次if先判断实例有没有被创建,如果没被创建就进入第一个if内,使一个线程成功获取锁(其余线程进行阻塞等待),线程获取锁后再次进行判断,判断实例是否创建,没有创建就进行创建。当这个实例创建完了之后,其他竞争到锁的线程就被里层 if 挡住了,也就不会继续创建其他实例。
阻塞队列
阻塞队列能是一种线程安全的数据结构, 并且具有以下特性:
- 当队列满的时候, 继续入队列就会阻塞, 直到有其他线程从队列中取走元素.
- 当队列空的时候, 继续出队列也会阻塞, 直到有其他线程往队列中插入元素.
阻塞队列的一种典型应用场景就是生产者消费者模型。
在 Java 标准库中内置了阻塞队列。 如果我们需要在一些程序中使用阻塞队列,直接使用标准库中的即可。
- BlockingQueue 是一个接口,真正实现的类是 LinkedBlockingQueue
- put 方法用于阻塞式的入队列,take 用于阻塞式的出队列
- BlockingQueue 也有 offer, poll, peek 等方法, 但这些方法不带有阻塞特性
下面我们来实现一个阻塞队列:
- 通过循环队列的方式
- 使用 synchronized 进行加锁控制
public class BlockingQueue {
private int[] arr = new int[1000];
private volatile int size = 0;
private int tail = 0;
private int head = 0;
public void put(int value) throws InterruptedException {
synchronized (this) {
while (size == arr.length) {
wait();
}
arr[tail] = value;
tail = (tail + 1) % arr.length;
size++;
notifyAll();
}
}
public int take() throws InterruptedException {
int ret = 0;
synchronized (this) {
while (size == 0) {
wait();
}
ret = arr[head];
head = (head + 1) % arr.length;
size--;
notifyAll();
}
return ret;
}
public static void main(String[] args) throws InterruptedException {
BlockingQueue bq = new BlockingQueue();
Thread t1 = new Thread(() -> {
try {
for (int i = 0; i < 10; i++) {
bq.put(i);
System.out.println("生产者放入:" + i);
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t1.start();
Thread t2 = new Thread(() -> {
try {
for (int i = 0; i < 10; i++) {
int num = bq.take();
System.out.println("消费者取出:" + num);
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t2.start();
t1.join();
t2.join();
}
}