万字解析设计模式之单例模式

news2024/11/15 18:48:59

一、概述

1.1简介

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象

1.2结构

单例模式的主要有以下角色:

  • 单例类。只能创建一个实例的类
  • 访问类。使用单例类

1.3单例模式的实现

单例设计模式分类两种:

饿汉式:类加载就会导致该单实例对象被创建

懒汉式:类加载不会导致该单实例对象被创建,而是首次使用该对象时才会创建

饿汉式-方式1(静态变量方式) 

package com.yanyu.Singleton;

public class Singleton {
    //私有构造方法
    private Singleton() {}
    //在成员位置创建该类的对象
    private static Singleton instance = new Singleton();
    //对外提供静态方法获取该对象
    public static Singleton getInstance() {
        return instance;
    }
}

这是一个单例模式的实现,确保在程序运行中只有一个该类的实例对象存在。

具体实现:

1. 将类的构造方法私有化,防止外部直接通过构造方法创建对象。

2. 在类的成员位置创建一个私有静态的对象 instance,确保了Singleton类不能在外部被实例化,只能在类内部创建对象。

3. 对外提供一个静态的方法 getInstance(),返回该类的对象 instance,确保在程序中只有一个该类的实例对象存在

4. 由于 instance 是私有的静态成员,在类加载时就已经创建了该对象,所以在 getInstance() 方法中直接返回 instance 即可。由于instance是static成员,类的所有对象共享同一份instance,从而保证了在应用中只有一个Singleton对象被创建。

需要注意的是,该实现方法并未考虑线程安全性,可能会存在线程安全问题

使用单例模式可以避免重复创建对象,节省内存空间,并且可以确保对象在程序中只有一个实例,保证数据一致性。

package com.yanyu.Singleton;

public class client{
    public static void main(String[]args){
        //创建singletion类的对象
        Singleton instance = Singleton.getInstance();
        Singleton instance1 = Singleton.getInstance();
            //判断获取到的两个是否是同一个对象
        System.out.println(instance == instance1);
    }
}

 说明:

该方式在成员位置声明Singleton类型的静态变量,并创建Singleton类的对象instance。instance对象是随着类的加载而创建的。如果该对象足够大的话,而一直没有使用就会造成内存的浪费。

饿汉式-方式2(静态代码块方式)

package com.yanyu.Singleton;

public class Singleton {
    //私有构造方法
    private Singleton() {}
    //在成员位置创建该类的对象
    private static Singleton instance;
    static {
        instance = new Singleton();
    }
    //对外提供静态方法获取该对象
    public static Singleton getInstance() {
        return instance;
    }
}

 这是一种饿汉式单例模式的实现方式,通过静态代码块来初始化单例对象,保证了线程安全性和唯一性。在类被加载时就已经创建了单例对象,因此也叫做饿汉式单例模式。

因为类加载是线程安全的,不需要考虑多线程的情况。

但是,由于对象是在类被加载时就创建的,因此可能会造成资源浪费。如果该单例对象在程序运行期间一直没有被使用,那么一直占据着一部分内存空间,会对系统的性能产生一定的影响。

另外,由于单例对象是静态的,所以对于某些需要动态实现的场景,该实现方式并不合适。

懒汉式-方式1(线程不安全)

package com.yanyu.Singleton;

public class Singleton {
    //私有构造方法
    private Singleton() {}
    //在成员位置创建该类的对象
    private static Singleton instance;
    //对外提供静态方法获取该对象
    public static Singleton getInstance() {
        if(instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

 具体实现是通过私有构造方法来禁止类的外部创建对象,然后在类的成员位置创建该类的唯一对象instance,并通过静态方法getInstance()来返回该对象。在getInstance()方法中,先判断instance是否为null,如果是则创建该对象,否则直接返回该对象。

这种实现方式称为懒汉式单例模式,因为只有在第一次调用getInstance()方法时才会创建对象,而之后的调用都会直接返回已创建的对象。从而避免了多个实例导致的资源浪费和数据不一致等问题。

 懒汉式-方式2(线程安全)

package com.yanyu.Singleton;

public class Singleton {
    //私有构造方法
    private Singleton() {}
    //在成员位置创建该类的对象
    private static Singleton instance;
    //对外提供静态方法获取该对象
    public static synchronized Singleton getInstance() {
        if(instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

 懒汉式相对于饿汉式来说,它是延迟加载的,只有在真正需要使用该对象时才会进行初始化,这样可以节省资源和减少初始化时间。但是,懒汉式实现需要考虑线程安全问题,因为在多线程环境下,如果多个线程同时调用 getInstance() 方法,可能会创建多个实例,导致单例模式失效。因此,需要使用 synchronized 关键字在方法内部进行同步,保证线程安全。

但是在getInstance()方法上添加了synchronized关键字,导致该方法的执行效果特别低

懒汉式-方式3(双重检查锁)

对于 getInstance() 方法来说,绝大部分的操作都是读操作,读操作是线程安全的,所以我们没必让每个线程必须持有锁才能调用该方法,我们需要调整加锁的时机。由此也产生了一种新的实现模式:双重检查锁模式 

package com.yanyu.Singleton;

public class Singleton {
    //私有构造方法
    private Singleton() {}
    private static Singleton instance;
    //对外提供静态方法获取该对象
    public static Singleton getInstance() {
        //第一次判断,如果instance不为null,不进入抢锁阶段,直接返回实际
        if(instance == null) {
            synchronized (Singleton.class) {
                //抢到锁之后再次判断是否为空
                if(instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

是怕多线程创建多个对象,如果不为空直接返回提高效率,为空就要加锁防止

首先进行一次非空判断,如果instance为null,才进行同步代码块的抢锁,再次判断instance是否为空,确保只有一个线程可以创建对象。

双重检查锁模式是一种非常好的单例实现模式,解决了单例、性能、线程安全问题,上面的双重检测锁模式看上去完美无缺,其实是存在问题,在多线程的情况下,可能会出现空指针问题,出现问题的原因是JVM在实例化对象的时候会进行优化和指令重排序操作。

要解决双重检查锁模式带来空指针异常的问题,只需要使用 volatile 关键字, volatile 关键字可以保证可见性和有序性。

指令重排序问题解释

在多线程情况下,指令重排序可能会导致该模式的失效。当一个线程抢到锁之后,由于指令重排序的影响,实例变量可能会先被赋值到内存中,但是还没有调用构造函数,而此时另一个线程进入该方法,会认为instance不为空直接返回实例,但是此时实例并没有完成初始化,会导致程序出错。

为了避免这种情况,需要在instance前添加volatile关键字,保证它能正确的被初始化。这样可以确保其他线程在获取instance的时候,总是从主内存中获取,而不是线程的本地内存中获取,从而避免了指令重排序带来的问题。

package com.yanyu.Singleton;

public class Singleton {
    //私有构造方法
    private Singleton() {}
    private static volatile Singleton instance;
    //对外提供静态方法获取该对象
    public static Singleton getInstance() {
        //第一次判断,如果instance不为null,不进入抢锁阶段,直接返回实际
        if(instance == null) {
            synchronized (Singleton.class) {
                //抢到锁之后再次判断是否为空
                if(instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

 这是一种线程安全的懒汉式单例模式实现方式,主要特点有:

1. 构造函数私有化,确保该类不能在外部通过构造函数来创建对象。

2. 使用 volatile 关键字修饰 instance,保证在多线程环境下 instance 的可见性。

3. getInstance 方法加入双重检查锁,确保在多线程环境下只有一个线程能够创建 Singleton 实例,并且只有在 instance 为 null 的情况下才会创建实例。

4. 使用 synchronized 关键字对 instance 进行加锁,保证在多线程环境下只有一个线程能够进入创建实例的代码块。

5. 返回 Singleton 实例的方法为静态方法,能够在类的外部方便地获取该对象。

这种实现方式既保证了线程安全,又减少了同步的开销,是一种比较常用的单例模式实现方式。

懒汉式-方式4(静态内部类方式)

静态内部类单例模式中实例由内部类创建,由于 JVM 在加载外部类的过程中, 是不会加载静态内部类的, 只有内部类的属性/方法被调用时才会被加载, 并初始化其静态属性。静态属性由于被 static 修饰,保证只被实例化一次,并且严格保证实例化顺序。

package com.yanyu.Singleton;

public class Singleton {

    //私有构造方法
    private Singleton() {}
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    //对外提供静态方法获取该对象
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

这是一种利用静态内部类实现的线程安全的单例模式。在类加载器加载类的时候,静态内部类 SingletonHolder 不会被初始化,只有在 getInstance() 方法第一次被调用时,才会被加载进内存并实例化,同时保证了线程安全。这种方式既保证了线程安全,也避免了同步带来的性能损失,同时也实现了延迟加载。由于这种方式不需要加锁,因此效率比较高

 这种方式的核心思想是使用静态内部类来持有单例实例,因为静态内部类只会被加载一次,所以它的成员变量也只会被初始化一次,从而保证了线程安全。同时,通过将单例对象的实例化延迟到内部类加载时进行,也实现了懒加载。

 枚举方式

枚举类实现单例模式是极力推荐的单例实现模式,因为枚举类型是线程安全的,并且只会装载一次,设计者充分的利用了枚举的这个特性来实现单例模式,枚举的写法非常简单,而且枚举类型是所用单例实现中唯一一种不会被破坏的单例实现模式。

package com.yanyu.Singleton;

public enum Singleton {
    INSTANCE;
}

 INSTANCE是一个枚举对象,也就是该单例类的唯一实例。通过这种方式获取单例对象,可以保证在任何情况下都只有一个实例对象存在。即使在多线程的情况下也是安全的。可以避免多线程问题和反射攻击

package com.yanyu.Singleton;

public class client{
    public static void main(String[]args){
        //创建singletion类的对象
        Singleton instance = Singleton.INSTANCE;
        Singleton instance1 = Singleton.INSTANCE;
            //判断获取到的两个是否是同一个对象
        System.out.println(instance == instance1);
    }
}

在使用枚举类型实现单例模式时,反射攻击是无效的,因为枚举类型的构造方法是私有的,并且只会在类加载时被调用一次,保证了单例的唯一性。在使用反射获取该单例时,会抛出异常,因为枚举类型不支持反射创建实例。因此,枚举类型实现单例模式是一种安全可靠的方式。

1.4破坏单例模式的方式 

使上面定义的单例类(Singleton)可以创建多个对象,枚举方式除外。有两种方式,分别是序列化和反射。

序列化反序列化

Singleton类:

package com.yanyu.Singleton;

import java.io.Serializable;

public class Singleton implements Serializable {

    //私有构造方法
    private Singleton() {}

    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    //对外提供静态方法获取该对象
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

Test类:

package com.yanyu.Singleton;


import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class Test {
    public static void main(String[] args) throws Exception {
        //往文件中写对象
        //writeObject2File();
        //从文件中读取对象
        Singleton s1 = readObjectFromFile();
        Singleton s2 = readObjectFromFile();
        //判断两个反序列化后的对象是否是同一个对象
        System.out.println(s1 == s2);
    }
    private static Singleton readObjectFromFile() throws Exception {
        //创建对象输入流对象
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\Users\\Think\\Desktop\\a.txt"));
        //第一个读取Singleton对象
        Singleton instance = (Singleton) ois.readObject();
        return instance;
    }
    public static void writeObject2File() throws Exception {
        //获取Singleton类的对象
        Singleton instance = Singleton.getInstance();
        //创建对象输出流
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\Think\\Desktop\\a.txt"));
        //将instance对象写出到文件中
        oos.writeObject(instance);
    }
}

上面代码运行结果是false,表明序列化和反序列化已经破坏了单例设计模式。

当单例模式中的实例被序列化成字节流并保存到文件系统或数据库中时,如果后续读取对象并反序列化后,会生成一个新的实例,从而破坏了单例模式的原则。

这是因为,在序列化和反序列化过程中,Java内部使用了一个特殊的方法来创建对象,该方法不会调用任何构造函数,也不会检查是否已经存在该对象的实例。因此,如果单例模式本身没有实现序列化和反序列化的特殊处理,就会导致破坏单例模式。

 序列化会将对象转换为一个字节序列,以便在网络上传输或保存到文件中。当反序列化时,会将字节序列转换回对象。当单例类被序列化为字节流时,字节流中不包含单例类的状态,当反序列化时,新的实例会被创建出来,这样就违反了单例模式的原则,一个单例类就被破坏了。

反射 

Singleton类:

package com.yanyu.Singleton;


public class Singleton {
    //私有构造方法
    private Singleton() {}

    private static volatile Singleton instance;
    //对外提供静态方法获取该对象
    public static Singleton getInstance() {
        if(instance != null) {
            return instance;
        }
        synchronized (Singleton.class) {
            if(instance != null) {
                return instance;
            }
            instance = new Singleton();
            return instance;
        }
    }
}

Test类:

package com.yanyu.Singleton;

import java.lang.reflect.Constructor;

public class Test {
    public static void main(String[] args) throws Exception {
        //获取Singleton类的字节码对象
        Class clazz = Singleton.class;
        //获取Singleton类的私有无参构造方法对象
        Constructor constructor = clazz.getDeclaredConstructor();
        //取消访问检查
        constructor.setAccessible(true);
        //创建Singleton类的对象s1
        Singleton s1 = (Singleton) constructor.newInstance();
        //创建Singleton类的对象s2
        Singleton s2 = (Singleton) constructor.newInstance();
        //判断通过反射创建的两个Singleton对象是否是同一个对象
        System.out.println(s1 == s2);
    }
}

上面代码运行结果是false,表明反射已经破坏了单例设计模式

 单例模式可以被反射机制破坏,因为反射机制可以通过修改类的私有构造方法来创建一个新的实例,这样就违背了单例模式的原则。此外,反射还可以通过修改单例类中的字段来改变单例实例的状态,从而破坏单例模式的行为。

1.5 问题的解决

序列化、反序列方式解决

在Singleton类中添加readResolve()方法,在反序列化时被反射调用,如果定义了这个方法,就返回这个方法的值,如果没有定义,则返回新new出来的对象。

Singleton类:

package com.yanyu.Singleton;

import java.io.Serializable;


public class Singleton implements Serializable {

    //私有构造方法
    private Singleton() {}
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    //对外提供静态方法获取该对象
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }

    /**
     * //当,进行反序列化时,会白动调用该力法,将该方法的返回值直接返回
     */
    private Object readResolve() {
        return SingletonHolder.INSTANCE;
    }
}

当反序列化时,会先调用 readObject() 方法,如果该类中存在 readResolve() 方法,会在 readObject() 方法执行之后,将 readResolve() 方法的返回值直接返回,从而确保只有一个实例。在该示例中,readResolve() 方法返回 SingletonHolder 的 INSTANCE,即单例对象的唯一实例。

反射方式解决

为了防止这种破解,可以在单例类的构造函数中添加判断,如果已存在实例,则抛出异常或返回已存在的实例。这样,在通过反射调用私有构造函数时,就会抛出异常或返回已存在的实例,从而阻止破解单例模式。

package com.yanyu.Singleton;


public class Singleton {
    private static boolean flag = false;
    //私有构造方法
    private Singleton() {
        /*
           反射破解单例模式需要添加的代码
        */
        synchronized (Singleton.class){
            if (flag){
                throw new RuntimeException("不能创建多个对象");
            }
            flag = true;
        }
    }

    private static volatile Singleton instance;
    //对外提供静态方法获取该对象
    public static Singleton getInstance() {
        if(instance != null) {
            return instance;
        }
        synchronized (Singleton.class) {
            if(instance != null) {
                return instance;
            }
            instance = new Singleton();
            return instance;
        }
    }
}

上述代码中的防止反射破解的代码可以阻止通过反射调用私有构造方法创建多个实例。但是,如果通过反射修改了flag标志位,就可以绕过这个限制,破坏单例模式。

为了进一步防止通过反射破解单例模式,可以在getInstance方法中添加判断,如果已存在实例,再次调用构造方法时,直接返回已存在的实例。这样,即使通过反射修改了flag标志位,也无法创建新的实例,从而保证单例模式的唯一性。

 1.6JDK源码解析-Runtime类

 从上面源代码中可以看出Runtime类使用的是恶汉式(静态属性)方式来实现单例模式的。

使用Runtime类中的方法


public class RuntimeDemo {
    public static void main(String[] args) throws IOException {
        //获取Runtime类对象
        Runtime runtime = Runtime.getRuntime();
​
        //返回 Java 虚拟机中的内存总量。
        System.out.println(runtime.totalMemory());
        //返回 Java 虚拟机试图使用的最大内存量。
        System.out.println(runtime.maxMemory());
​
        //创建一个新的进程执行指定的字符串命令,返回进程对象
        Process process = runtime.exec("ipconfig");
        //获取命令执行后的结果,通过输入流获取
        InputStream inputStream = process.getInputStream();
        byte[] arr = new byte[1024 * 1024* 100];
        int b = inputStream.read(arr);
        System.out.println(new String(arr,0,b,"gbk"));
    }
}

1.7应用案例

  • Windows 的 Task Manager(任务管理器)。
  • Windows 的 Recycle Bin(回收站)。在整个系统运行过程中,回收站一直维护着仅有的一个实例。
  • 网站的计数器,一般也是采用单例模式实现,否则难以同步。
  • 应用程序的日志应用,一般都何用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。
  • Web 应用的配置对象的读取,一般也应用单例模式,这个是由于配置文件是共享的资源。

二、实验

任务描述

在企业网站后台系统中,一般会将网站统计单元进行独立设计,比如登录人数的统计、IP 数量的计数等。在这类需要完成全局统计的过程中,就会用到单例模式,即整个系统只需要拥有一个计数的全局对象。

本关任务:模拟网站登录,高并发场景。模拟 10 个登录线程,程序输出登录总数。

实现要点

  1. 在类中添加一个私有静态成员变量用于保存单例实例。
  2. 声明一个公有静态构建方法用于获取单例实例。
  3. 在静态方法中实现"延迟初始化"。 该方法会在首次被调用时创建一个新对象, 并将其存储在静态成员变量中。 此后该方法每次被调用时都返回该实例。
  4. 将类的构造函数设为私有。 类的静态方法仍能调用构造函数, 但是其他对象不能调用。
  5. 检查客户端代码, 将对单例的构造函数的调用替换为对其静态构建方法的调用。

编程要求

本任务有三个文件“Login.java”、“Client.java”和“Singleton.java”,在右侧编辑器 Begin-End 内补充 Singleton 中代码,其它文件请阅读代码

Singleton.java 

package step1;

import java.util.concurrent.atomic.AtomicLong;

public class Singleton {
    private static Singleton instance;
    //AtomicLong是以原子方式操作long值的类,作用是保证并发时线程安全的累加
    private AtomicLong count = new AtomicLong(0);
    /********** Begin *********/
    //此处增加Singleton的构造函数
     
    private Singleton() {
    // 私有化构造函数,防止外部实例化
    }
    /********** End *********/
    public static Singleton GetInstance(){
        /********** Begin *********/
        //考虑线程安全问题
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
    }
    return instance;

         /********** End *********/
    }
    public AtomicLong getCount() {
        return count;
    }
    public void setCount() {
        count.addAndGet(1);
    }
}

 Login.java

package step1;
//Runnable 接口由其实现类来由线程执行对应的实例。对于实现类必须是实现方法 run
public class Login implements Runnable{
    private String loginname;
    public String getLoginname() {
        return loginname;
    }
    public void setLoginname(String loginname) {
        this.loginname = loginname;
    }
    @Override
    public void run() {
        Singleton lazySingleton =Singleton.GetInstance();
        lazySingleton.setCount();
        /*调试时观察
        System.out.println(getLoginname()+"登录成功."+lazySingleton);
        */
    }
}

Client.java

package step1;

public class Client {
    public final static int num = 10;

    public static void main(String[] args) throws InterruptedException {
        
        ///创建10个线程,模拟10个用户登录
        Thread[] threads = new Thread[num];
        for (int i = 0; i < num; i++) {
            Login login = new Login();
            login.setLoginname("" + String.format("%2s", (i + 1)) + "号用户");
            //创建了线程threads[i],并把login对象(已实现Runnable接口)放入线程中
            threads[i]=new Thread(login);
            //线程状态转换为RUNNABLE
            threads[i].start();
        }
        for (Thread thread : threads) {
            //Client等待线程结束之后才能继续运行,防止最后一行的System.out.println提前运行
            thread.join();
        }
        System.out.println("网站共有"+Singleton.GetInstance().getCount()+"个用户登录");
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1143176.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MyBatis-Plus 实战教程二 核心功能

这里写目录标题 核心功能条件构造器QueryWrapperUpdateWrapperLambdaQueryWrapper 自定义SQL基本用法多表关联 Service接口CRUD基本用法Lambda批量新增 仓库地址 核心功能 条件构造器 除了新增以外&#xff0c;修改、删除、查询的SQL语句都需要指定where条件。因此BaseMapper…

6 个最佳 Windows 免费磁盘分区管理器

几乎所有新的笔记本电脑和 PC 都只有一个分区 C:\&#xff0c;与安装了 Windows 的分区相同。不太精通技术的用户开始按照计算机呈现给他们的方式使用计算机&#xff1b;他们将所有文档、个人文件&#xff08;例如图片、歌曲、电影等&#xff09;放在同一个分区上。整个驱动器上…

vite中将css,js文件归类至文件夹

build: {chunkSizeWarningLimit: 1500,rollupOptions: {output: {// 最小化拆分包manualChunks(id) {if (id.includes(node_modules)) {return id.toString().split(node_modules/)[1].split(/)[0].toString()}},// 用于从入口点创建的块的打包输出格式[name]表示文件名,[hash]…

github搜索技巧探索

毕设涉及到推荐系统&#xff0c;那么就用搜索推荐系统相关资料来探索一下GitHub的搜搜技巧 文章目录 1. 基础搜索2. 限定在特定仓库搜索3. 按照语言搜索4. 按照star数量搜索5. 搜索特定用户/组织的仓库6. 查找特定文件或路径7. 按时间搜索8. 搜索不包含某个词的仓库9. 搜索特定…

python:多波段遥感影像分离成单波段影像

作者:CSDN @ _养乐多_ 在遥感图像处理中,我们经常需要将多波段遥感影像拆分成多个单波段图像,以便进行各种分析和后续处理。本篇博客将介绍一个用Python编写的程序,该程序可以读取多波段遥感影像,将其拆分为单波段图像,并保存为单独的文件。本程序使用GDAL库来处理遥感影…

Android-登录注册页面(第三次作业)

第三次作业 - 登录注册页面 题目要求 嵌套布局。使用线性布局的嵌套结构&#xff0c;实现登录注册的页面。&#xff08;例4-3&#xff09; 创建空的Activity 项目结构树如下图所示&#xff1a; 注意&#xff1a;MainActivity.java文件并为有任何操作&#xff0c;主要功能集中…

Android中 BufferQueue 和 Gralloc

目录 零、本篇讨论范围一、图片数据流的生产者与消费者1.1 生产者1.2 消费者 二、生产者与消费者间数据的传递2.1 BufferQueue2.2 Gralloc 零、本篇讨论范围 接上篇 SurfaceFlinger做Layer合成时&#xff0c;如何与HAL层进行交互 后&#xff1a; 本篇的讨论范围如下图红框中所…

selenium 根据【关键词】获取知网文献信息

哈喽大家好&#xff0c;我是咸鱼 之前咸鱼写过几篇关于知网爬虫的文章&#xff0c;后台反响都很不错。虽然但是&#xff0c;咸鱼还是忍不住想诉苦一下 有些小伙伴文章甚至代码看都没看完&#xff0c;就问我 ”为什么只能爬这么多条文献信息&#xff1f;“&#xff08;看过代码…

Python Selenium 之数据驱动测试的实现!

数据驱动模式的测试好处相比普通模式的测试就显而易见了吧&#xff01;使用数据驱动的模式&#xff0c;可以根据业务分解测试数据&#xff0c;只需定义变量&#xff0c;使用外部或者自定义的数据使其参数化&#xff0c;从而避免了使用之前测试脚本中固定的数据。可以将测试脚本…

从瀑布模式到水母模式:ChatGPT如何赋能软件研发全流程

文章目录 前言内容简介作者简介专家推荐读者对象直播预告 前言 计算机技术的发展和互联网的普及&#xff0c;使信息处理和传输变得更加高效&#xff0c;极大地改变了金融、商业、教育、娱乐等领域的运作方式。数据分析、人工智能和云计算等新兴技术&#xff0c;也在不断地影响和…

影响光源的因素

影响光源的因素 对比度 1.对比度 均匀性 2.均匀性 色彩还原性 3.色彩还原性 其他因素&#xff1a; 4. 亮度 &#xff1a; 光源 亮度是光源选择时的重要参考&#xff0c;尽量选择亮度高的光源。 5. 鲁棒性 &#xff1a; 鲁棒性是指光源是否对部件的位置敏感度最小 。 6. 光…

Leetcode 剑指 Offer II 050. 路径总和 III

题目难度: 中等 原题链接 今天继续更新 Leetcode 的剑指 Offer&#xff08;专项突击版&#xff09;系列, 大家在公众号 算法精选 里回复 剑指offer2 就能看到该系列当前连载的所有文章了, 记得关注哦~ 题目描述 给定一个二叉树的根节点 root &#xff0c;和一个整数 targetSum…

大厂面试题-什么是内存溢出,什么是内存泄漏?

目录 1、什么是内存溢出&#xff1f; 2、什么是内存泄漏&#xff1f; 3、如何避免&#xff1f; 1、什么是内存溢出&#xff1f; 我们来看到右侧的区域&#xff0c;假设我们JVM中可用的内存空间只剩下3M&#xff0c;但是我们要创建一个5M的对象&#xff0c;那么&#xff0c;…

前端JS for循环内异步接口变成同步提交(JavaScript for循环异步变同步)

遇见的问题&#xff1a; 导入Excel文件的时候&#xff0c;将每行数据整合成一个数组&#xff0c;循环数组插入每一条数据&#xff0c;插入数据后要判断是否插入成功&#xff0c;如果没插入成功的话&#xff0c;停止循环&#xff0c;不再插入后面的数据。甚至插入数据后&#xf…

【Leetcode】反转单链表

反转单链表 反转单链表题目题目思路代码 反转单链表题目 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 题目思路 链表的本质就是改变每一个结点的next域。 我们从第一个结点开始遍历&#xff0c;改变它的next域。 当我们要注意在改变…

尚未解决:use_python()和use_virtualenv()的使用

reticulate包为Python和R之间的互操作性提供了一套全面的工具。该包包含以下功能&#xff1a; 以多种方式从R调用Python&#xff0c;包括RMarkdown、获取Python脚本、导入Python模块以及在R会话中交互使用Python。 R和Python对象之间的转换&#xff08;例如&#xff0c;R和Pan…

TP项目启用websocket聊天功能 - gateway、wss、swoole、长连接 - PHP

TP项目启用websocket聊天功能 须知 swoole不支持windows安装,没有windows扩展WebSocket 在线测试(可测本地wss连接) websocket在线测试建议gateway只负责给终端发信,不参与逻辑部分后台负责所有的收信+发信安排,可以方便地获取用户好友关系、上下线状态管理、消息缓存、已…

图文并茂的帮助文档你值得拥有

概述 工作中除了写代码开发需求&#xff0c;也需要写文档&#xff0c;怎么写好一个文档能够让读者既能看懂API&#xff0c;又能快速上述操作&#xff0c;所见即所得。本文基于vitepress、ace-builds带大家实现一个这样好用的帮助文档。 实现效果 在线预览地址&#xff1a;ht…

4.5 数据加密

思维导图&#xff1a; 4.5 数据加密 为确保高度敏感数据的安全性&#xff0c;如财务、军事及国家机密数据&#xff0c;可采用数据加密技术。此技术将原始数据&#xff08;明文&#xff09;转化为不可识别格式&#xff08;密文&#xff09;&#xff0c;确保不知解密方法的人无法…

提高车联远控异常分析效率的设想

提高车联远控异常分析效率的设想 前言 随着汽车集成度、智能化、软件功能越来越丰富&#xff0c;用户车辆使用已不是传统的出行、驾驶等物理场景&#xff0c;更多的人与车的互动功能的场景。其中车联远控功能使用日益增多。技术人员开展排查车联远控问题时&#xff0c;往往需…