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

 为什么要用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));
    }
}

 注意: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("发送失败了捏");
        }
    }
}

注意:

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

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

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

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

举个例子

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

为此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);
        }
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Alphamilk

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值