首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >多线程案例

多线程案例

作者头像
独断万古他化
发布2026-01-15 13:07:24
发布2026-01-15 13:07:24
50
举报
文章被收录于专栏:Java 攻略Java 攻略

一、 设计模式

  • 单例模式 单例模式能保证某个类在程序中只存在唯一一份实例, 而不会创建出多个实例
1.1 饿汉模式

类加载的时候创建实例

代码语言:javascript
复制
//此处要求singleton 类只能有一个实例
//饿汉模式,在类加载的时候进行创建实例:被JVM加载到内存得到类对象时:类似为程序启动
class Singleton {
    //加上static 之后,当前的成员变为类属性,在类对象上,类对象只有一个实例
    private static Singleton instance = new Singleton();

    public static Singleton getInstance(){
        return instance;
    }

    //单例模式最关键的要点,禁止构造方法被外部使用
    private Singleton() {

    }
}

public class Demo1 {
    public static void main(String[] args) {
        Singleton s1 = Singleton.getInstance();
    }
}

饿汉模式在多线程中并没有安全问题,因为是多个线程进行读取。

1.2 懒汉模式

第一次使用的时候创建实例

代码语言:javascript
复制
//懒汉模式
class SingletonLazy{
    private static SingletonLazy instance = null;

    //懒汉模式关键:创建时机推迟到第一次使用的时候创建
    public static SingletonLazy getInstance(){
        if(instance == null){
            instance = new SingletonLazy();
        }
        return instance;
    }

    private SingletonLazy(){

    }
}


public class Demo2 {
    public static void main(String[] args) {
        SingletonLazy s1 = SingletonLazy.getInstance();
        SingletonLazy s2 = SingletonLazy.getInstance();

        System.out.println(s1 == s2);
    }
}

此时的设计在多线程中是有问题的,只在第一次使用时会出现线程安全问题。可以进行加锁操作。

代码语言:javascript
复制
//懒汉模式
class SingletonLazy{
    private static SingletonLazy instance = null;
    private static Object locker = new Object();

    //懒汉模式关键:创建时机推迟到第一次使用的时候创建
    public static SingletonLazy getInstance(){
        synchronized (locker){
            if(instance == null){
                instance = new SingletonLazy();
            }
        }

        return instance;
    }

    private SingletonLazy(){

    }
}

但是此处一进入方法就会加锁,而线程安全问题只在实例化之前存在线程安全问题,一旦实例化完毕之后,线程安全问题就没有了,所以实例化之后仍然加锁多个线程调用就会产生阻塞,效率产生影响,因此还可以进行改进。

代码语言:javascript
复制
//懒汉模式
class SingletonLazy{
    private static 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(){

    }
}
  • 此时还是存在一些问题的,存在的问题为指令重排序,也是之前所说的=线程安全问题的一种;什么是指令重排序呢,它也是编译器的一种优化手段,可以调整你指令执行的顺序,从而使代码的效率更高。
  • 而这段代码的指令重排序的原因是因为 instance = new SingletonLazy(); 这行代码涉及到的指令非常多,这只是一个简化的模型,而构造方法内部需要做哪些事情不知道。将这个代码分为三个步骤:1.分配内存,2.针对空间进行初始化,3.内存空间首地址,赋值到引用变量中。1和2操作中内部都可能含有很多指令。而指令重排序可能将这个逻辑执行顺序改为1、3、2,此时就会出现下面的问题。如果一个线程在执行了1和2的操作后被调度到其他线程,而此时instance已经被赋值了所以不为空,但指向的对象是一个没有被初始化的空白的对象,在其他线程进行判断时,发现不为空直接返回了,此时其他线程可能还会继续执行,拿着没有进行初始化的instance进行相关操作就可能发生问题。
  • 因此需要禁止指令重排序,把这样的编译器优化关闭掉,需要用到volatile 关键字,(内存可见性 / 指令重排序都是出现在针对某个变量的读写操作中)当使用volatile 修饰 instance 的时候,此时围绕instance 变量进行读写操作的优化就都被关闭了,确保从内存进行读写,确保操作顺序不会被打乱。

那么最终保证线程安全的代码为:

代码语言:javascript
复制
//懒汉模式
class SingletonLazy{
    private volatile static 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(){

    }
}

二、阻塞队列

2.1 阻塞队列概述

阻塞队列是一种特殊的队列,也遵守“先进先出”的原则,会在队列已满或者队列为空时进入阻塞,阻塞队列是一种线程安全的数据结构,并且具有以下特性:

  • 当队列满的时候,继续入队列会产生阻塞,只到有其他线程从队列中取走元素
  • 当队列空的时候,继续出队也会产生阻塞,只到有其他线程往队列中插入元素
2.2 生产者消费者模型

阻塞队列的典型应用场景是" 生产者消费者模型 ",产生的作用:

  1. 降低资源的竞争
  2. 解耦合 作为生产者消费者模式的缓存空间,将线程之间分隔,通过阻塞队列间接联系起来使解耦合。
  3. 削峰填谷 阻塞队列相当于一个缓冲区,平衡了生产者和消费者的处理能力,阻塞队列本身的大小是有限的,所以起到一个限制作用。

使用阻塞队列的例子:

代码语言:javascript
复制
public class Demo4 {
    //简单的生产者消费者模型
    //一个线程表示生产者,一个线程表示消费者
    //搞一个阻塞队列,生产者往阻塞队列中放一个整数,消费者进行获取

    public static void main(String[] args) throws InterruptedException {
        BlockingQueue<Long>  queue = new ArrayBlockingQueue<>(1000);

        //生产者
        Thread t1 = new Thread(() -> {
            long n = 0;
            while (true){
                try {
                    queue.put(n);
                    System.out.println("生产了:" + n);
                    n++;
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        //消费者
        Thread t2 = new Thread(() -> {

            while (true){
                try {
                    long n = queue.take();
                    System.out.println("消费了:" + n);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        t1.start();
        t2.start();
    }
}

其中,生产者t1线程生产一个值,消费者 t2线程才会消费一个值。

2.3 阻塞队列实现
代码语言:javascript
复制
//只保存Sting类型
class MyBlockingQueue {
    private String[] data;
    String val = null;
    //[head,tail)构成循环队列的有效区间
    private int head = 0;
    private int tail = 0;
    //size解决满
    private int size = 0;

    private Object locker = new Object();

    public MyBlockingQueue(int capacity){
        data = new String[capacity];
    }

    public void put(String elem) throws InterruptedException {
        synchronized(locker){
            while (size == data.length){
                //如果满了则进行等待唤醒
                locker.wait();
            }

            data[tail] = elem;
            tail++;
            if(tail >= data.length){
                tail = 0;
            }
            size++;
            locker.notify();
        }
    }

    public String take() throws InterruptedException {
        synchronized (locker){
            while (size == 0){
                locker.wait();
            }

            //取出head元素
            String result = data[head];
            head++;
            if(head >= data.length){
                head = 0;
            }
            size--;
            locker.notify();
            return result;
        }
    }
}

public class Demo5 {
    public static void main(String[] args) {
        MyBlockingQueue queue = new MyBlockingQueue(1000);

        Thread t1 = new Thread(() -> {
            long n = 0;
            while (true){
                try {
                    queue.put(n + "");
                    System.out.println("生产了:" + n);
                    n++;
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        Thread t2 = new Thread(() -> {
           while (true){
               try {
                   String n = queue.take();
                   System.out.println("消费了:" + n);
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   throw new RuntimeException(e);
               }
           }
        });

        t1.start();
        t2.start();
    }
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2026-01-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、 设计模式
    • 1.1 饿汉模式
    • 1.2 懒汉模式
  • 二、阻塞队列
    • 2.1 阻塞队列概述
    • 2.2 生产者消费者模型
    • 2.3 阻塞队列实现
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档