深入理解Java单例模式和优化多线程任务处理

目录

单例模式能保证某个类在程序中只存在唯一一份实例, 而不会创建出多个实例,并提供一个全局访问点。

饿汉模式

类加载的同时,创建实例。

java 复制代码
class  Singleton {
    private static final Singleton instance = new Singleton();
    //将构造方法设为私有,以防止外部通过new关键字创建新的实例。
    private Singleton() {}
    public static Singleton getInstance() {
        return instance;
    }
}
  • 上述代码定义了一个名为Singleton的类。
  • 在类中定义了一个私有的静态常量instance,它是Singleton类的一个唯一实例。
  • 提供了一个公共的静态方法getInstance(),用于获取Singleton类的唯一实例。

懒汉模式

类加载的时候不创建实例,第一次使用的时候才进行创建。

单线程版

java 复制代码
class Singleton {
    private static Singleton instance = null;
    private Singleton() {
    }
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

多线程版

上述单线程代码在多线程中就会出现错误,多个线程同时调用getInstance()方法时,就可能导致创建出多个实例是不安全的。这里我们只需要在getInstance()方法中添加synchronized关键字就可解决。

java 复制代码
class Singleton {
    private static Singleton instance = null;
    private Singleton() {
    }
    public synchronized static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

双重检查锁定

java 复制代码
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 进行加锁控制
java 复制代码
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();
    }
}
相关推荐
先生沉默先30 分钟前
Nginx 反向代理学习:单端口统一访问多服务
学习·nginx·arcgis
♡喜欢做梦30 分钟前
MyBatis XML 配置文件:从配置规范到 CRUD 开发实践
xml·java·java-ee·mybatis
爱吃烤鸡翅的酸菜鱼33 分钟前
Spring Boot 实现 WebSocket 实时通信:从原理到生产级实战
java·开发语言·spring boot·后端·websocket·spring
J不A秃V头A34 分钟前
Maven的分发管理与依赖拉取
java·maven
雪域迷影37 分钟前
C++中编写UT单元测试用例时如何mock非虚函数?
开发语言·c++·测试用例·gmock·cpp-stub开源项目
AI街潜水的八角1 小时前
Python电脑屏幕&摄像头录制软件(提供源代码)
开发语言·python
hadage2332 小时前
--- git 的一些使用 ---
开发语言·git·python
LFly_ice3 小时前
学习React-24-路由传参
前端·学习·react.js
陈天伟教授3 小时前
基于学习的人工智能(3)机器学习基本框架
人工智能·学习·机器学习·知识图谱
毕设源码-钟学长3 小时前
【开题答辩全过程】以 高校课程学习评价系统设计与实现为例,包含答辩的问题和答案
学习