Java常用类库中(ThreadLocal、Comparable比较器、AutoCloseable、Optional空处理)附带相关面试题

简介: 1.ThreadLocal线程独立,2.Comparable比较器与Comparetor,3.AutoCloseable接口,4.Optional空处理



1.ThreadLocal线程独立

如果说每一个用户代表一个线程,那么当用户去访问自己的独有的比如id时候就应该正确返回自己的id。ThreadLocal就好比一个大储物柜里面有许多小的储物柜,每一个线程拥有自己的储物柜,方便自己拿取自己的东西,这样就能保证数据之间的独立与安全

ThreadLocal类常用的方法:

方法 描述
T get() 返回当前线程关联的变量副本的值。如果没有设置过,则返回null
void set(T value) 将当前线程关联的变量副本设置为指定的值。
void remove() 删除当前线程关联的变量副本。
protected T initialValue() 提供一个初始化值,用于首次访问时创建变量副本的情况。子类可以重写此方法来指定自定义的初始化值。
static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) 创建一个带有初始值提供者的ThreadLocal实例。每个线程在首次访问时,都会使用提供者生成一个初始值。
static ThreadLocalRandom current() 返回当前线程关联的ThreadLocalRandom实例

案例要求:

设置三个线程ABC每一个线程要有独立的资源。并且每个线程输出自己独立的资源

案例代码以及分析:

package Example1511;
class Message {
    private String inFo;
    public void setInFo(String inFo) {
        this.inFo = inFo;
    }
    public String getInFo() {
        return inFo;
    }
}
class Channel {
    private static final ThreadLocal<Message> ThreadLocal = new ThreadLocal<Message>();
    //    构造方法独立,防止外部访问
    private Channel() {
    }
    //    存放对象到储物盒子ThreadLocal里面
    public static void setMessage(Message msg) {
        ThreadLocal.set(msg);
    }
    //    获取自己盒子里面的对象的信息
    public static void send() {
        System.out.println(Thread.currentThread().getName() + "获取信息:" + ThreadLocal.get().getInFo());
    }
}
public class javaDemo {
    public static void main(String[] args) {
        new Thread(() -> {
            Message msg = new Message();
            msg.setInFo("我是线程A特有的资源哦");
            Channel.setMessage(msg);
            Channel.send();
        }, "线程A").start();
        new Thread(() -> {
            Message msg = new Message();
            msg.setInFo("我是线程B特有的资源哦");
            Channel.setMessage(msg);
            Channel.send();
        }, "线程B").start();
        new Thread(() -> {
            Message msg = new Message();
            msg.setInFo("我是线程C特有的资源哦");
            Channel.setMessage(msg);
            Channel.send();
        }, "线程C").start();
    }
}

image.gif

image.gif编辑

为什么要用private static final ThreadLocal<Message> ThreadLocal = new ThreadLocal<Message>();static final修饰为了什么目的?

在这段代码中,使用private static final ThreadLocal<Message> ThreadLocal = new ThreadLocal<Message>();ThreadLocal声明为私有静态常量。

private修饰符表示ThreadLocal只能在Channel类内部访问,禁止外部直接访问,从而保证了对ThreadLocal的访问权限的封装性。

static修饰符表示ThreadLocal是一个类级别的变量,而不是实例级别的变量。这意味着所有的Channel实例共享同一个ThreadLocal对象,而不是每个实例都拥有自己的副本。这样做的目的是为了确保线程间的隔离效果。

final修饰符表示ThreadLocal引用的对象是不可变的,一旦引用被赋值,就无法再更改其指向的对象。这里将ThreadLocal声明为常量是为了防止意外修改ThreadLocal的引用,确保程序的稳定性和可靠性。

总的来说,将ThreadLocal声明为private static final的目的是为了实现线程间的资源隔离并确保其访问权限的封装、共享和稳定性。

面试题:ThreadLocal 是什么?有哪些使用场景?

    1. ThreadLocal 是一个本地线程副本变量工具类,在每个线程中都创建了一个 ThreadLocalMap 对象,简单说 ThreadLocal 就是一种以空间换时间的做法,每个线程可以访问自己内部 ThreadLocalMap 对象内的 value。通过这种方式,避免资源在多线程间共享。
    2. 原理:线程局部变量是局限于线程内部的变量,属于线程自身所有,不在多个线程间共享。Java提供ThreadLocal类来支持线程局部变量,是一种实现线程安全的方式。但是在管理环境下(如 web 服务器)使用线程局部变量的时候要特别小心,在这种情况下,工作线程的生命周期比任何应用变量的生命周期都要长。任何线程局部变量一旦在工作完成后没有释放,Java 应用就存在内存泄露的风险。
    3. 经典的使用场景是为每个线程分配一个 JDBC 连接 Connection。这样就可以保证每个线程的都在各自的 Connection 上进行数据库的操作,不会出现 A 线程关了 B线程正在使用的 Connection; 还有 Session 管理 等问题。

    面试题:请谈谈 ThreadLocal 是怎么解决并发安全的?

      1. 在java程序中,常用的有两种机制来解决多线程并发问题,一种是sychronized方式,通过锁机制,一个线程执行时,让另一个线程等待,是以时间换空间的方式来让多线程串行执行。而另外一种方式就是ThreadLocal方式,通过创建线程局部变量,以空间换时间的方式来让多线程并行执行。两种方式各有优劣,适用于不同的场景,要根据不同的业务场景来进行选择。
      2. 在spring的源码中,就使用了ThreadLocal来管理连接,在很多开源项目中,都经常使用ThreadLocal来控制多线程并发问题,因为它足够的简单,我们不需要关心是否有线程安全问题,因为变量是每个线程所特有的。

      面试题: 很多人都说要慎用 ThreadLocal,谈谈你的理解,使用 ThreadLocal 需要注意些什么?

      ThreadLocal 变量解决了多线程环境下单个线程中变量的共享问题,使用名为ThreadLocalMap的哈希表进行维护(key为ThreadLocal变量名,value为ThreadLocal变量的值);

      使用时需要注意以下几点:

      线程之间的threadLocal变量是互不影响的,

      使用private final static进行修饰,防止多实例时内存的泄露问题

      线程池环境下使用后将threadLocal变量remove掉或设置成一个初始值


      2.Comparable比较器与Comparetor

      在现实生活中一个班级有很多个人数,也就意味着有许多个对象,如果经历一次考试之后我需要对考试后大家对象数组进行排序,需要成绩从高到低排序,比如黄小龙 成绩总分60 ,蓝小龙 成绩总分50,绿小龙 成绩总分 40等等,可以通过Comparable比较器去设置比较再进行排序

      Comparable接口常用的方法

      方法 描述
      int compareTo(T other) 将当前对象与另一个对象进行比较。返回一个负整数、零或正整数,表示当前对象小于、等于或大于另一个对象。
      boolean equals(Object obj) 检查当前对象是否与另一个对象相等。
      default Comparator<T> reversed() 返回当前比较器的逆序(降序)比较器。默认方法,可以在实现Comparable接口的类中使用。
      default Comparator<T> thenComparing(...) 返回一个比较器,该比较器首先使用当前比较器进行比较,然后使用其他指定的比较器进行进一步比较。默认方法,可以在实现Comparable接口的类中使用。

      注意:

      实现接口需要覆写CompareTo方法,该方法有三个返回值

        • return  1 -->表示结果大于
        • return -1-->表示结果小于
        • return  0 -->表示结果相等

        案例要求:经历一场考试后,班级需要对考试班里的同学排名的张贴告示,要求成绩从高到底,每个对象需要有以下信息:名字,学号,成绩总分。以下是本次班级考试结果

        张三,210101,560

        李四 ,  210108,   589

        王五 ,  210116,   340

        赵六,   210153,   623

        请用数组对象输出成绩从高到低

        案例代码:

        package Example1512;
        import org.jetbrains.annotations.NotNull;
        import java.util.Arrays;
        class Student implements Comparable<Student>{
            private int id;
            private String name;
            private int score;
            Student(String name,int id,int score){
                this.name = name;
                this.id = id;
                this.score = score;
            }
        //    覆写比较方法
            @Override
            public int compareTo(@NotNull Student o) {
                if (this.score<o.score){
                    return 1;
                }
                else if (this.score>o.score){
                    return -1;
                }else return 0;
            }
            @Override
            public String toString() {
                return "姓名:"+name + " 学号:" + id +" 总分:"+score;
            }
        }
        public class javaDemo {
            public static void main(String[] args) {
                Student[] allStudent= new Student[]{
                        new Student("张三",210101,560),
                        new Student("李四 ",210108,589),
                        new Student("王五 ",210116,340),
                        new Student("赵六",210153,623)
                };
                Arrays.sort(allStudent);
                System.out.println(Arrays.toString(allStudent));
            }
        }

        image.gif

        image.gif编辑

        注意:Arrays类中引用了Comparable的比较方法CompareTo才能用sort方法


        3.AutoCloseable接口

        由于资源是有限的,随着程序的扩大对资源越来越多的需求,资源会越来越紧张,所以为了保证资源的合理使用,一些资源使用后就不太需要的时候,就需要进行释放所以引入接口AutoCloseable进行自动的资源释放

        AutoCloseable接口的常用方法:

        方法 描述
        void close() throws Exception 关闭资源的方法,需要在实现类中实现。使用完资源后,应该调用该方法来释放资源。注意,close()方法可能会抛出异常,因此在调用时应该进行异常处理。
        default void addSuppressed(Throwable exception) 将一个异常附加到当前正在关闭的异常上。在关闭资源时,如果出现了其他异常,可以通过该方法将其添加到关闭资源的异常中,以便于统一处理。
        default void suppressedExceptions(Throwable... exceptions) 抑制指定的一组异常。在关闭资源时,如果出现了多个异常,可以通过该方法将它们全部抑制起来。

        案例要求与案例代码:

        package Example1513;
        interface Message extends AutoCloseable{
            public void send();
        }
        class NetMessage implements Message{
            private String Info;
        //    构造函数
            NetMessage(String Info){
                this.Info = Info;
            }
        //    判断是否正常建立连接
            public boolean open(){
                System.out.println("建立通信");
                return true;
            }
            @Override
            public void send() {
                if (open()){
                        if (Info.contains("马冬梅")){
                            throw new RuntimeException();
                        };
                    System.out.println("发送消息:"+Info);
                }
            }
            @Override
            public void close() throws Exception {
                System.out.println("自动关闭消息通道");
            }
        }
        public class javaDemo {
            public static void main(String[] args) {
                try (Message msg = new NetMessage("大爷");) {
                    msg.send();
                }catch (Exception e){
                    System.out.println("发送失败了捏");
                }
            }
        }

        image.gif

        image.gif编辑

        注意:

        要实现自动执行close()方法,可以使用try-with-resources语句块,它会在代码块结束后自动调用资源的close()方法来释放资源。

        public static void main(String[] args) {
            try (Message msg = new NetMessage("大爷")) {
                msg.send();
            } catch (Exception e) {
                System.out.println("发送失败了捏");
            }
        }

        image.gif


        4.(重点掌握)Optional空处理

        在引用对象中存在Null,但是如果调用一个类的静态方法,该方法实现需要一个实例化对象的值,那么此时会出现空异常(NullPointerException)

        举个例子

        String str = null;
        int length = str.length(); // 触发空指针异常,因为str对象未初始化

        image.gif

        为此Java提供Optional空处理来减少空指针异常的情况

        以下是Optional的常用方法

        方法 描述
        of(T value) 创建一个包含指定非空值的Optional对象
        ofNullable(T value) 创建一个包含指定值的Optional对象,如果值为空,则创建包含空值的Optional对象
        empty() 创建一个空的Optional对象
        isPresent() 判断Optional对象是否包含非空值
        get() 获取Optional对象中的非空值
        orElse(T other) 如果Optional对象中存在非空值,则返回该值;否则返回指定的默认值
        orElseGet(Supplier<? extends T> otherSupplier) 如果Optional对象中存在非空值,则返回该值;否则通过Supplier提供的方法生成并返回默认值
        orElseThrow(Supplier<? extends X> exceptionSupplier) 如果Optional对象中存在非空值,则返回该值;否则根据Supplier提供的方法抛出异常
        ifPresent(Consumer<? super T> consumer) 如果Optional对象中存在非空值,则对该值执行指定操作
        filter(Predicate<? super T> predicate) 如果Optional对象中存在非空值,并且满足指定条件,则返回当前对象;否则返回空的Optional对象
        map(Function<? super T,? extends U> mapper) 如果Optional对象中存在非空值,则对该值应用指定的映射函数,并返回包含映射结果的Optional对象
        flatMap(Function<? super T,Optional<U>> mapper) 如果Optional对象中存在非空值,则对该值应用指定的映射函数,并返回映射结果的Optional对象;否则返回空的Optional对象

        案例代码:

        package Example1515;
        import java.util.Optional;
        interface Message{
            public String getContent();
        }
        class MessageImp implements Message{
            @Override
            public String getContent() {
                return "输出获取到的非空对象信息";
            }
        }
        class MessageUtil{
        //    私立化构造方法防止外部调用
            private MessageUtil(){}
        //    获取非空对象
            public static Optional<Message> getMessageImp(){
                return Optional.of(new MessageImp());
            }
        //    获取内容
            public static void useMessage(Message msg){
                System.out.println(msg.getContent());
            }
        }
        public class javaDemo {
            public static void main(String[] args) {
                Optional<Message> test= MessageUtil.getMessageImp();
                if (test.isPresent()){
                    Message temp = test.get();
                    MessageUtil.useMessage(temp);
                }
            }
        }

        image.gif

        image.gif编辑


        目录
        相关文章
        |
        1天前
        |
        存储 Java
        面试官:素有Java锁王称号的‘StampedLock’你知道吗?我:这什么鬼?
        面试官:素有Java锁王称号的‘StampedLock’你知道吗?我:这什么鬼?
        43 23
        |
        1天前
        |
        安全 Java 开发者
        Java一分钟之-Optional类:优雅处理null值
        【5月更文挑战第13天】Java 8的`Optional`类旨在减少`NullPointerException`,提供优雅的空值处理。本文介绍`Optional`的基本用法、创建、常见操作,以及如何避免错误,如直接调用`get()`、误用`if (optional != null)`检查和过度使用`Optional`。正确使用`Optional`能提高代码可读性和健壮性,建议结合实际场景灵活应用。
        19 3
        |
        1天前
        |
        消息中间件 安全 前端开发
        字节面试:说说Java中的锁机制?
        Java 中的锁(Locking)机制主要是为了解决多线程环境下,对共享资源并发访问时的同步和互斥控制,以确保共享资源的安全访问。 锁的作用主要体现在以下几个方面: 1. **互斥访问**:确保在任何时刻,只有一个线程能够访问特定的资源或执行特定的代码段。这防止了多个线程同时修改同一资源导致的数据不一致问题。 2. **内存可见性**:通过锁的获取和释放,可以确保在锁保护的代码块中对共享变量的修改对其他线程可见。这是因为 Java 内存模型(JMM)规定,对锁的释放会把修改过的共享变量从线程的工作内存刷新到主内存中,而获取锁时会从主内存中读取最新的共享变量值。 3. **保证原子性**:锁
        16 1
        |
        1天前
        |
        Java
        【Java多线程】面试常考 —— JUC(java.util.concurrent) 的常见类
        【Java多线程】面试常考 —— JUC(java.util.concurrent) 的常见类
        23 0
        |
        1天前
        |
        安全 Java 程序员
        【Java多线程】面试常考——锁策略、synchronized的锁升级优化过程以及CAS(Compare and swap)
        【Java多线程】面试常考——锁策略、synchronized的锁升级优化过程以及CAS(Compare and swap)
        12 0
        |
        1天前
        |
        Java
        三个可能的Java面试题
        【5月更文挑战第7天】Java垃圾回收机制自动管理内存,回收无引用对象的内存,确保内存有效利用。多态性允许父类引用操作不同子类对象,如Animal引用可调用Dog的方法。异常处理机制通过try-catch块捕获和处理程序异常,例如尝试执行可能导致ArithmeticException的代码,catch块则负责处理异常。
        36 9
        |
        1天前
        |
        Java
        【JAVA面试题】static的作用是什么?详细介绍
        【JAVA面试题】static的作用是什么?详细介绍
        |
        1天前
        |
        Java
        【JAVA面试题】final关键字的作用有哪些
        【JAVA面试题】final关键字的作用有哪些
        |
        1天前
        |
        JavaScript 前端开发 Java
        【JAVA面试题】什么是引用传递?什么是值传递?
        【JAVA面试题】什么是引用传递?什么是值传递?
        |
        算法 Java 开发者
        21. 你能说说Java中Comparable和Comparator的区别吗
        21. 你能说说Java中Comparable和Comparator的区别吗
        60 0