深入理解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类的唯一实例。

懒汉模式

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

单线程版

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();
    }
}


相关文章
|
8天前
|
安全 Java 开发者
深入解读JAVA多线程:wait()、notify()、notifyAll()的奥秘
在Java多线程编程中,`wait()`、`notify()`和`notifyAll()`方法是实现线程间通信和同步的关键机制。这些方法定义在`java.lang.Object`类中,每个Java对象都可以作为线程间通信的媒介。本文将详细解析这三个方法的使用方法和最佳实践,帮助开发者更高效地进行多线程编程。 示例代码展示了如何在同步方法中使用这些方法,确保线程安全和高效的通信。
28 9
|
8天前
|
监控 安全 Java
Java中的多线程编程:从入门到实践####
本文将深入浅出地探讨Java多线程编程的核心概念、应用场景及实践技巧。不同于传统的摘要形式,本文将以一个简短的代码示例作为开篇,直接展示多线程的魅力,随后再详细解析其背后的原理与实现方式,旨在帮助读者快速理解并掌握Java多线程编程的基本技能。 ```java // 简单的多线程示例:创建两个线程,分别打印不同的消息 public class SimpleMultithreading { public static void main(String[] args) { Thread thread1 = new Thread(() -> System.out.prin
|
10天前
|
安全 Java
Java多线程集合类
本文介绍了Java中线程安全的问题及解决方案。通过示例代码展示了使用`CopyOnWriteArrayList`、`CopyOnWriteArraySet`和`ConcurrentHashMap`来解决多线程环境下集合操作的线程安全问题。这些类通过不同的机制确保了线程安全,提高了并发性能。
|
11天前
|
Java
java小知识—进程和线程
进程 进程是程序的一次执行过程,是系统运行的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行到消亡的过程。简单来说,一个进程就是一个执行中的程序,它在计算机中一个指令接着一个指令地执行着,同时,每个进程还占有某些系统资源如CPU时间,内存空间,文件,文件,输入输出设备的使用权等等。换句话说,当程序在执行时,将会被操作系统载入内存中。 线程 线程,与进程相似,但线程是一个比进程更小的执行单位。一个进程在其执行的过程中产生多个线程。与进程不同的是同类的多个线程共享同一块内存空间和一组系统资源,所以系统在产生一个线程,或是在各个线程之间做切换工作时,负担要比
22 1
|
3月前
|
存储 监控 Java
Java多线程优化:提高线程池性能的技巧与实践
Java多线程优化:提高线程池性能的技巧与实践
116 1
|
6月前
|
设计模式 监控 Java
Java多线程基础-11:工厂模式及代码案例之线程池(一)
本文介绍了Java并发框架中的线程池工具,特别是`java.util.concurrent`包中的`Executors`和`ThreadPoolExecutor`类。线程池通过预先创建并管理一组线程,可以提高多线程任务的效率和响应速度,减少线程创建和销毁的开销。
210 2
|
6月前
|
Java 数据库
【Java多线程】对线程池的理解并模拟实现线程池
【Java多线程】对线程池的理解并模拟实现线程池
59 1
|
3月前
|
安全 算法 Java
17 Java多线程(线程创建+线程状态+线程安全+死锁+线程池+Lock接口+线程安全集合)(下)
17 Java多线程(线程创建+线程状态+线程安全+死锁+线程池+Lock接口+线程安全集合)
77 6
|
3月前
|
存储 安全 Java
17 Java多线程(线程创建+线程状态+线程安全+死锁+线程池+Lock接口+线程安全集合)(中)
17 Java多线程(线程创建+线程状态+线程安全+死锁+线程池+Lock接口+线程安全集合)
86 5
|
3月前
|
存储 安全 Java
17 Java多线程(线程创建+线程状态+线程安全+死锁+线程池+Lock接口+线程安全集合)(上)
17 Java多线程(线程创建+线程状态+线程安全+死锁+线程池+Lock接口+线程安全集合)
84 3