单列模式
单例模式能保证某个类在程序中只存在唯⼀⼀份实例, ⽽不会创建出多个实例
1.饿汉模式
只要程序一启动就会立即创建出一个对象
class Signleton{
private static Signleton instance=new Signleton();
//防止在以后的代码中再创建对象,我们将构造方法private,
private Signleton(){
}
public static Signleton getInstance(){
return instance;
}
}
注意虽然构造方法是private,但是我们仍然可以在类外面用反射拿到私有构造方法,创建实例
如果在代码中存在多个单例类,饿汉模式在程序启动的时候就会发生扎堆创建,延缓了程序启动时间。
2.懒汉模式
只有在调用的时候才会创建对象
class SingletonLazy {
private static volatile SingletonLazy instance=null;
private SingletonLazy(){
}
public static void getInstance(){
if(instance==null){
synchronized (SingletonLazy.class){
if(instance==null){
instance=new SingletonLazy();
}
}
}
}
}
懒汉模式进阶
class Singleton {
//在这里我们加入volatile,第一点为了内存可见性问题,第二点为了指令重排序问题
private static volatile Singleton instance = null;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
假设有两个线程,t1,t2.线程之间是强占是执行的,t1,t2都通过第一层判断(instance==null),假设t1拿到锁,在t1执行完返回一个instance,这是instance不再是null,所以在t1线程释放锁后,t2线程将不再进入第二层的判断,故这个对象只创建了一次。这种双重if就避免了重复创建对象
指令重排序问题
此程序代码大体可以三个步骤:
1.申请内存空间
2.调用构造方法
3.把此时内存空间的地址,幅值给instance引用
如上图在t1和t2线程中,如果在t1线程执行了第一步和第二步,没有把第三步执行完就去执行线程t2,此时instance这个引用已经不为空了,也就是说返回创建的对象根本就没有创建好。
阻塞队列
在 Java 标准库中内置了阻塞队列. 如果我们需要在⼀些程序中使⽤阻塞队列, 直接使⽤标准库中的即
阻塞队列相比较于普通队列和优先级队列来说,线程是安全的,而其他两个线程是不安全的
- BlockingQueue 是⼀个接⼝. 真正实现的类LinkedBlockingQueue.
- put ⽅法⽤于阻塞式的⼊队列, take ⽤于阻塞式的出队列.
- BlockingQueue 也有 offer, poll, peek 等⽅法, 但是这些⽅法不带有阻塞特性.可.
BlockingQueue<String> queue = new LinkedBlockingQueue<>();
// ⼊队列
queue.put("abc");
// 出队列. 如果没有 put 直接 take, 就会阻塞.
String elem = queue.take();
这种就阻塞了
public class demo2 {
public static void main(String[] args) throws InterruptedException {
BlockingQueue<String> queue=new ArrayBlockingQueue<>(1000);
queue.put("aaa");
String elem= queue.take();
System.out.println("elem="+elem);
queue.take();
}
}
用阻塞队列实现消费者模型
public static void main(String[] args) throws InterruptedException {
BlockingQueue<Integer> blockingQueue=new LinkedBlockingQueue<>();
Thread custom=new Thread(()->{
while (true){
try {
int value= blockingQueue.take();
System.out.println("消费者:"+value);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
},"消费者");
custom.start();
Thread producer=new Thread(()->{
Random random=new Random();
while (true){
try {
int num=random.nextInt(1000);
blockingQueue.put(num);
System.out.println("生产者:"+num);
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
},"生产者");
producer.start();
custom.join();
producer.join();
}
}
消费者模型有很多优势,其中两个最重要的是
1.解耦合(线程只关心与队列的交互,不用管线程之间的交互)
2.削峰填谷(大概意思就是假如是两个线程,这两个线程相互制约,以最慢的为准,你生产一个我去消费一个)