多线程单例模型阻塞队列
多线程——单例模型、阻塞队列
一、单例模型
单例模式,是 设计模式 (针对问题使用不同方式解决问题)中一种非常经典的模式,而单例模式就如其名字一样,强制要求 一个类不能创建多个对象 。
而单例模型大概分俩种,一种是 饿汉模式 ,一种是 懒汉模式 。
1、饿汉模式
饿汉模式,听名字就知道是非常饥饿,而在java中的意思就是在
类加载的同时,创建实例 。
class Singleton{
private static Singleton instance=new Singleton(100);
public static Singleton getInstance() {
return instance;
}
private Singleton(){
}
private Singleton(int n){
}
}
而在过程中,只能返回一个实例,不能返回多个,不然会报错,这一点在很多场景上都需要. 比如 JDBC 中的 DataSource 实例就只需要一个。
2、懒汉模式
而懒汉模式和饿汉模式恰恰相反,懒汉模式是
使用的时候再创建实例 ,不使用不创建实例。同时,懒汉模式也有分为单线程和多线程类。单线程如下:
class SingletonLazy{
private static SingletonLazy instance=null;
public static SingletonLazy getInstance2(){
if(instance==null){
instance=new SingletonLazy();
}
return instance;
}
private SingletonLazy(){
}
}
在上述中懒汉模式是需要用的时候再创建实例,不需要就不用,对于单线程来说这个是可以的,但对于多线程中的话,因为多线程不是 原子性 的,所以在同时调用getInstance2方法的话,就很有可能会导致创建多个实例,所以,为了杜绝这个问题,我们需要加上 synchronized 来改善这里的线程安全问题。
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(){
}
}
用双重if来降低锁竞争,然后再用voliatile解决可见性问题,这样的话就能解决线程安全问题。
二、阻塞队列
1、阻塞队列原理
阻塞队列是什么?阻塞队列其实就是一种特殊的队列,遵守 “先进先出” 的原则。
我们以图为例子:
这是一个很普通的客户端A向服务器B发起请求,如果没有阻塞队列的话,那么就直接全部数据一起传输,如果有阻塞的话,就会变成下面情况:
在客户端A向客户端B发出请求的时候,中间有阻塞队列, 如果里面队满(阻塞队列存储满),而不能够入队列,直到队列不满。如果队列为空,则不能够出队列,直到队列不为空。
2、阻塞队列常见用法
阻塞队列最常用的就是put和take方法, put方法就是将元素添加到阻塞队列,而take则是拿出来 ,有一点十分注意的是,阻塞队列中有offer的方法,但使用这个是不会被阻塞的,就会变成一个普通的队列。
public static void main(String[] args) throws InterruptedException {
BlockingQueue<String> queue=new LinkedBlockingQueue<>(10);
for (int i = 0; i < 10; i++) {
queue.put("aaa");
}
System.out.println("队列已满");
for (int i = 0; i < 10; i++) {
System.out.println(queue.take());
}
System.out.println("队列为空");
queue.take();
}
3、生产者消费者模型
而我们对于阻塞队列中有关的的有生产者消费者模型,这个案例十分典型,解释一下什么是生产者消费者模型,在一个阻塞队列中,生产速度大于消费速度,那么当
生产量和消费量的差值已经是阻塞队列的大小之后,那么就会进行阻塞 ,直到保证生产量和消费量的差值比阻塞队列的大小还小,这样就能做到生产者和消费者之间的硬性平衡。以下面为案例:
public static void main(String[] args) {
BlockingQueue<Integer> queue=new LinkedBlockingQueue<>(4);
Thread producer=new Thread(()->{
int n=0;
while (true){
try {
queue.put(n);//offer不会阻塞,put才行
System.out.println("生产元素"+n);
n++;
Thread.sleep(1000);
}catch(InterruptedException e){
throw new RuntimeException(e);
}
}
},"producer");
Thread consumer=new Thread(()->{
while(true){
try{
Integer n=queue.take();
System.out.println("消费元素"+n);
Thread.sleep(3000);
}catch(InterruptedException e){
throw new RuntimeException(e);
}
}
},"consumer");
producer.start();
consumer.start();
}
在上面图中,生产的速度是一秒一个,而消费的速度是三秒一个,但阻塞队列的大小为4,所以当生产元素减去消费元素>=4的时候,就会进行阻塞,直到差值小于4.