单例模式
单例模式,顾名思义就是保证在一个程序中某个类只能创建一个实例的模式。
单例模式又是设计模式中的一种,啥又是设计模式?例如:象棋游戏,当你和电脑下棋,你每走一步棋电脑都会有对应的策略回应,这种针对性的问题场景有固定的套路。
饿汉模式
饿汉模式是单例模式的一种。饿汉,就是一个饿了很久的人,看见有吃的马上就酷酷吃了起来。饿汉模式就是类加载的时候同时创建一个实例。
class SingletonHuggery{private static SingletonHuggery sgh = new SingletonHuggery();private SingletonHuggery(){}public static SingletonHuggery Gethuggery(){return sgh;}
}
懒汉模式
区别于饿汉模式,懒汉模式在类加载的时候不会自动创建实例,只有在第一次使用的时候创建实例。
//懒汉模式(单线程版本)
class Singleton{private static Singleton instance = null;private Singleton(){}public Singleton getInstance(){if(instance == null)instance = new Singleton();return instance;}
}
由于一个程序中只允许存在唯一一份实例,所以创建实例这件事在多个线程之间需要存在竞争关系,我们可以通过给方法加锁来加强多个线程在创建实例这件事上的互斥性。
//懒汉模式 (多线程版本)
class Singleton {private static Singleton instance = null;private Singleton(){}public synchronized Singleton getSingleton(){if(instance == null){instance = new Singleton();}return instance;}
}
但是,通过给方法加锁的方法产生的锁竞争太过于强烈了,如何降低这一点呢?我们不在方法上加锁,而是在内部创建实例的这个地方加锁,当实例已经存在,线程到不了获取锁的这部分代码,也就减小了极大的锁竞争。
class SingletonLazy {private static volatile SingletonLazy instance = null;private static Object locker = new Object();public static SingletonLazy getInstance() {if (instance == null) {synchronized (locker) {if (instance == null) {instance = new SingletonLazy();}}}return instance;}private SingletonLazy(){}
}
优化后,实例的可见性提高了,有利于降低多个线程对于锁的竞争性~
生产者消费者模式
阻塞队列:
阻塞队列是一种线程安全的数据结构,遵循“先进先出”的规则。
当队列为满,继续入队那么队列会阻止后续线程入队列,让其阻塞等待到队列有元素被取出
当队列为空,继续出队那么队列也会阻塞,直到有其他的元素入队列为止
而生产者消费者模式就是很典型的一个阻塞队列案例~
生产者消费者模式:
阻塞队列在这个模式当中相当于一个缓冲区,当没有线程要消费元素时,生产的元素就会放在缓冲区,当缓冲区满了之后不再生产,当有线程消费元素时从缓冲区取出元素,缓冲区为空时无法消费~
public static void main(String[] args) throws InterruptedException {BlockingDeque<Integer> queue = new LinkedBlockingDeque<>(1000);Thread Consumer = new Thread(()->{while(true){try {int num = 0;num = queue.take();System.out.println("消费元素:"+num);Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});Consumer.start();Thread Productor = new Thread(()->{int i = 0;while (true){try {int val = i;queue.put(val);i++;System.out.println("生产元素:"+val);} catch (InterruptedException e) {e.printStackTrace();}}});Productor.start();Consumer.join();Productor.join();}
我们可以发现消费者和生产者的差值恒小于等于队列的最大容量
模拟实现阻塞队列:
实现阻塞队列的关键在于——当队列满的时候再尝试put就产生阻塞,当队列为空的时候尝试take产生阻塞,而在有与元素出队之后唤醒put,在有元素入队之后唤醒take产生的阻塞~
public class BlockingQueue {int[] array = new int[1000];private volatile int head = 0;private volatile int tail = 0;private volatile int size = 0;//入队列public void put(int val) throws InterruptedException {synchronized (this) {while(size == array.length){this.wait(); //注意此处阻塞要用this进行阻塞,否则下面另外一个方法中的notify()会同时唤醒阻塞}array[tail] = val;tail = (tail+1)% array.length;size++;this.notify();}}//出队列public int take() throws InterruptedException {int ret = 0;synchronized (this){while(size == 0){this.wait();}ret = array[head];head = (head+1)% array.length;size--;this.notify();}return ret;}public synchronized int size(){return size;}public static void main(String[] args) throws InterruptedException {BlockingQueue queue = new BlockingQueue();Random random =new Random(); //产生随机数生成元素Thread producter = new Thread(()->{while(true){int val = random.nextInt(1000);try {queue.put(val);System.out.println("生产元素:"+val);} catch (InterruptedException e) {e.printStackTrace();}}});producter.start();Thread consumer = new Thread(()->{while(true){int val = 0;try {val = queue.take();System.out.println("消费元素:"+val);Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});consumer.start();producter.join();consumer.join();}
}