前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >springboot第59集:面试官万字挑战,一文让你走出微服务迷雾架构周刊

springboot第59集:面试官万字挑战,一文让你走出微服务迷雾架构周刊

作者头像
达达前端
发布2024-02-25 09:29:08
1050
发布2024-02-25 09:29:08
举报
文章被收录于专栏:达达前端
代码语言:javascript
复制
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class SynchronizedArrayListExample {
    public static void main(String[] args) {
        // 创建一个线程安全的 ArrayList
        List<Integer> synchronizedList = Collections.synchronizedList(new ArrayList<>());

        // 创建并启动多个线程同时向 ArrayList 中添加元素
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    synchronized (synchronizedList) {
                        synchronizedList.add(j);
                    }
                }
            }).start();
        }

        // 等待所有线程执行完毕
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 输出 ArrayList 中的元素个数
        System.out.println("Size of synchronized ArrayList: " + synchronizedList.size());
    }
}

在高并发的情况下,多个线程同时操作 ArrayList 可能会引发线程不安全的问题,主要有以下几个原因:

  1. 非线程安全的操作: ArrayList 不是线程安全的数据结构,它的内部结构不是线程安全的。在多线程环境下,多个线程同时对 ArrayList 进行添加、删除、修改等操作可能会导致内部状态混乱,从而产生不可预知的结果。
  2. 并发修改异常: 当一个线程正在对 ArrayList 进行修改操作(如添加、删除元素)时,另一个线程也同时对 ArrayList 进行修改操作,可能会导致并发修改异常(ConcurrentModificationException)。

为了避免这些问题,通常需要在多线程环境下使用线程安全的数据结构,或者采用同步机制来保护共享数据的访问。下面是一个示例,演示了在多线程环境下操作 ArrayList 可能引发的线程不安全问题:

代码语言:javascript
复制
import java.util.ArrayList;

public class UnsafeArrayListExample {
    public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList<>();

        // 创建并启动多个线程同时向 ArrayList 中添加元素
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    arrayList.add(j);
                }
            }).start();
        }

        // 等待所有线程执行完毕
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 输出 ArrayList 中的元素个数
        System.out.println("Size of ArrayList: " + arrayList.size());
    }
}

栈溢出通常是由于以下原因之一导致的:

  1. 递归调用:递归调用的层数过多,导致函数调用栈空间不足,从而引发栈溢出错误。
  2. 大量循环或死循环:如果程序中存在大量循环或者死循环,并且循环次数过多,会导致栈空间不断增长,最终导致栈溢出。
  3. 全局变量过多:如果程序中定义了大量的全局变量,会增加栈空间的压力,可能导致栈溢出。
  4. 数据结构过大:如果程序中使用的数据结构(如数组、列表、映射等)过大,会占用大量内存空间,增加栈空间的压力,可能导致栈溢出。

下面是一些示例代码,演示了可能导致栈溢出的情况:

  1. 递归调用:
  2. 大量循环或死循环:
  3. 全局变量过多:
  4. 数据结构过大:
代码语言:javascript
复制
public class StackOverflowExample {
    public static void main(String[] args) {
        recursiveCall(0);
    }

    public static void recursiveCall(int n) {
        recursiveCall(n + 1);
    }
}

fifinally中最好不要包含return,否则程序会提前退出,返回值不是try或catch中保存的返回值。

代码语言:javascript
复制
public class Main {
    public static void main(String[] args) {
        System.out.println(test());
    }

    public static int test() {
        try {
            System.out.println("In try block");
            return 1;
        } catch (Exception e) {
            System.out.println("In catch block");
            return 2;
        } finally {
            System.out.println("In finally block");
        }
    }
}

在 Java 中,无论在 try 块中是否有 return 语句,finally 块都会执行。finally 块通常用于释放资源或执行清理操作,无论 try 块中是否发生异常,都会执行 finally 块。

使用 BigDecimal 类可以避免浮点数精度问题,确保得到精确的计算结果。

代码语言:javascript
复制
import java.math.BigDecimal;

public class Main {
    public static void main(String[] args) {
        BigDecimal result = new BigDecimal("3").multiply(new BigDecimal("0.1"));
        System.out.println(result); // 输出:0.3
    }
}

在 Java 中,3 * 0.1 应该返回 0.3,但由于浮点数的精度问题,可能会出现计算结果不准确的情况。这是因为在计算机中,浮点数的表示方式是有限的,而某些十进制小数无法精确地表示为二进制小数。

因此,当我们执行 3 * 0.1 这样的计算时,可能会出现一个非精确的结果。在实际测试中,可能会得到 0.30000000000000004 或者 0.29999999999999999 这样的结果,而不是精确的 0.3。这是由于浮点数的精度问题导致的。

为了避免由于浮点数精度问题导致的误差,通常建议在需要精确计算的场景中,使用 BigDecimal 类进行计算。

使用序列化机制创建对象(需要实现 Serializable 接口):

代码语言:javascript
复制
import java.io.*;

public class SerializationExample {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        // 使用序列化机制创建对象
        MyClass obj1 = new MyClass();
        // 将对象写入到字节流中
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(obj1);
        // 从字节流中读取对象
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
        MyClass obj2 = (MyClass) objectInputStream.readObject();
        obj2.printMessage(); // 输出:Hello, world!
    }
}

使用 clone() 方法创建对象(需要实现 Cloneable 接口):

代码语言:javascript
复制
public class CloneExample {
    public static void main(String[] args) throws CloneNotSupportedException {
        // 使用 clone() 方法创建对象
        MyClass obj1 = new MyClass();
        MyClass obj2 = (MyClass) obj1.clone();
        obj2.printMessage(); // 输出:Hello, world!
    }
}

使用 new 关键字创建新对象:

代码语言:javascript
复制
public class NewObjectExample {
    public static void main(String[] args) {
        // 使用 new 关键字创建新对象
        MyClass obj = new MyClass();
        obj.printMessage(); // 输出:Hello, world!
    }
}

class MyClass {
    public void printMessage() {
        System.out.println("Hello, world!");
    }
}

使用反射机制创建对象:

代码语言:javascript
复制
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class ReflectionExample {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        // 使用反射机制创建对象
        Class<MyClass> cls = MyClass.class;
        Constructor<MyClass> constructor = cls.getConstructor();
        MyClass obj = constructor.newInstance();
        obj.printMessage(); // 输出:Hello, world!
    }
}
代码语言:javascript
复制
import java.util.concurrent.ConcurrentHashMap;

public class Main {
    public static void main(String[] args) {
        // 创建一个 ConcurrentHashMap
        ConcurrentHashMap<Integer, String> concurrentHashMap = new ConcurrentHashMap<>();

        // 创建并启动多个线程
        for (int i = 0; i < 5; i++) {
            int finalI = i;
            new Thread(() -> {
                // 向 ConcurrentHashMap 中添加元素
                for (int j = 0; j < 10; j++) {
                    concurrentHashMap.put(finalI * 10 + j, "Value_" + finalI + "_" + j);
                    System.out.println(Thread.currentThread().getName() + " added: " + finalI * 10 + j);
                }
            }).start();
        }

        // 等待所有线程执行完毕
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 输出 ConcurrentHashMap 中的所有元素
        System.out.println("ConcurrentHashMap: " + concurrentHashMap);
    }
}

当需要在多线程环境中操作时,可以使用线程安全的 ConcurrentHashMap

Hashtable 是线程安全的,但在性能上可能不如 HashMap,因为 Hashtable 中的方法使用了 synchronized 关键字进行同步,这会造成一定的性能开销。因此,在不需要线程安全保证的情况下,推荐使用 HashMap,在需要线程安全保证的情况下,再考虑使用 Hashtable 或者 ConcurrentHashMap

代码语言:javascript
复制
import java.util.Hashtable;

public class Main {
    public static void main(String[] args) {
        // 创建一个 Hashtable
        Hashtable<Integer, String> hashtable = new Hashtable<>();

        // 创建并启动多个线程
        for (int i = 0; i < 5; i++) {
            int finalI = i;
            new Thread(() -> {
                // 向 Hashtable 中添加元素
                for (int j = 0; j < 10; j++) {
                    hashtable.put(finalI * 10 + j, "Value_" + finalI + "_" + j);
                    System.out.println(Thread.currentThread().getName() + " added: " + finalI * 10 + j);
                }
            }).start();
        }

        // 等待所有线程执行完毕
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 输出 Hashtable 中的所有元素
        System.out.println("Hashtable: " + hashtable);
    }
}

Hashtable 是线程安全的,因为它的每个方法都使用了 synchronized 关键字进行同步,这使得它可以直接用于多线程环境中。

目录和文件

创建目录

mkdir 名称 => mkdir /data

创建目录及子目录

mkdir -p 名称 => mkdir -p /data/node

创建一个或多个(用空格分开即可)

touch 文件1 文件2 => touch 1.txt 2.txt

复制文件

cp 文件 目录 => cp 1.txt /opt/data

复制文件并改名

cp 文件 目录 => cp 1.txt /opt/data/2.txt

移动目录到另一个目录

mv 目录 目录 => mv data /opt

移动目录到另一个目录并改名

mv 目录 目录 => mv data /opt/data2

强制删除一个目录

rm -rf data

文件夹授予权限

chmod 777 -R 目录 => chmod 777 -R data

解压tar.gz

tar -zxvf 压缩包 => tar -zxvf 1.tar.gz

解压zip

unzip 压缩包 => unzip 1.zip

查询目录路径

pwd

查看文件

cat 目录 => cat 1.txt

编辑文件

vi 目录 => vi 1.txt

将xxx写入文件

echo 内容 >> 文件 => echo '111' >> 1.txt

输出文件尾部内容

tail -n 行数 文件 => tail -n 1000 1.txt

查看文件

find /-name 文件 => find /-name 1.txt #网络

重启网络

service network restart #防火墙

关闭防火墙

systemctl stop firewalld.service

重启防火墙

systemctl restart firewalld.service

启动防火墙

systemctl start firewalld.service

防火墙状态

systemctl status firewalld.service

开启开机自启动

systemctl enable firewalld.service

关闭开机自启动

systemctl disable firewalld.service

查看已开放端口

firewalld-cmd --list-ports

开放端口(永久有效)(需要重新加载防火墙)

firewalld-cmd --zone=public --add-port=端口/tcp --permanent => firewalld-cmd --zone=public --add-port=8080/tcp --permanent

重新加载防火墙

firewalld-cmd --reload #服务

查看服务开机启动状态

systemctl list-run-files

关闭指定服务自启动

systemctl disable 服务 => systemctl disable mysql

开启指定服务自启动

systemctl enable 服务 => systemctl enable mysql #磁盘

查看磁盘

df -h #进程

查看端口

netstat -ntlp

进程启动情况

ps -ef|grep 进程名 => ps -ef|grep java

查看端口占用

netstat -tunlp|grep 端口 => netstat -tunlp|grep 8080

查看进程

ps -aux

终止进程

kill 进程 => kill 884

强制终止进程

kill -9 进程 => kill -9 884 #CPU

查看cpu情况

top #账号

切换账号

su 账号 => su root

新增账号

useradd 用户名 => useradd mysql

添加到分组

useradd -g 组名 用户名 => useradd -g mysql-group mysql

设置密码

passwd 用户名 => passwd mysql

删除用户

userdel 用户名 => userdel mysql

登录用户信息

whomi

Docker

查看内核

uname -r

启动docker

systemctl start docker

查看docker状态

systemctl status docker

重启docker

systemctl restart docker

查看版本

docker version

查看信息

docker info

获取帮助

docker --help

查看镜像

docker images

启动镜像

docker run -d -p 对外端口:容器端口 镜像名称 => docker run -d -p 6379:6379 redis

查看日志

docker logs 容器id => docker logs xz2wxdf

搜索镜像

docker search 镜像名称 => docker search jdk

打包镜像

docker tag 镜像名称:标签 => docker tag redis:7.0.1

删除镜像

docker rmi 镜像id => docker rmi dxfdxzsa

进入容器

docker exec -it 容器id /bin/bash => docker exec -it xsdfds /bin/bash

重启容器

docker restart 容器id => docker restart xsddf

列出容器

docker ps --a

停止容器

docker stop 容器id => docker stop exfds

删除容器

docker rm 容器id => docker rm xsdfds

强制停止容器

docker kill 容器id

查看容器内部细节

docker inspect 容器id

查看所有卷情况

docker volume ls

查看某个卷

docker volume inspect 卷名 => docker volume inspect /data

构建镜像

dokcer build -t 镜像名称:标签 . => docker build -t jdk:21 . #docker-compose

构建镜像

docker-compose build

构建镜像(不带缓存构建)

docker-compose build --no-cache

查看docker镜像

docker-compose images

启动所有镜像

docker-compose up -d

查看所有编排容器(包括已停止容器)

docker-compose ps -a

进入指定容器

docker-compose exec 容器名 bash => docker-compose exec nginx bash

停止所有启动容器

docker-compose stop

停止所有启动容器并删除

docker-compose down

停止某一个容器

docker-compose stop 容器名称 => docker-compose stop nginx

启动某一个容器

docker-compose up -d 容器名称 => docker-compose up -d nginx

重启某一个容器

docker-compose restart 容器名称 => docker-compose restart nginx

删除所有容器

docker-compose rm

查看容器日志

docker-compose logs -f 容器名称 => docker-compose logs -f nginx

查看容器运行进程

docker-compose top

yaml配置

nacos使用https和http协议,只要注册临时服务,都是走RPC,因此使用https需要改为https

代码语言:javascript
复制
spring:
  cloud:
    # nacos
    nacos:
      discovery:
        server-addr: https://127.0.0.1:8848
        namespace: public
        username: nacos
        password: nacos
        group: LAOKOU_GROUP
        # https
        secure: true
        # true 临时 false 持久
        ephemeral: true
      config:
        server-addr: https://127.0.0.1:8848
        namespace: public
        username: nacos
        password: nacos
        # 指定读取的文件格式
        file-extension: yaml
        group: LAOKOU_GROUP
        refresh-enabled: true

keytool命令详解

1.生成pfx证书(.p12是pfx的新格式)

代码语言:javascript
复制
keytool -genkey 
       -alias laokou-register # 证书别名,不区分大小写
       -storetype PKCS12 # 密钥库类型,常用类型有JKS、PKCS12
       -keyalg RSA # 密钥算法,可选密钥算法:RSA\DSA,默认DSA
       -keysize 2048 # 密钥长度(RSA=2048,DSA=2048)
       -keystore scg-keystore.p12  # 密钥库文件名称
       -validity 3650 # 证书有效天数

2.导出证书为cer(cer/crt是证书的公钥格式,cer是crt证书的微软形式)

代码语言:javascript
复制
keytool -exportcert -v 
        -alias laokou-register # 证书别名,不区分大小写
        -keystore scg-keystore.p12  # 密钥库文件名称
        -storepass laokou # 密钥库口令,推荐与keypass一致(获取keystore信息所需要密码)
        -file register.cer # 导出的文件名
代码语言:javascript
复制
import java.util.Hashtable;
import java.util.Enumeration;

public class Main {
    public static void main(String[] args) {
        // 创建一个 Hashtable
        Hashtable<Integer, String> hashtable = new Hashtable<>();

        // 添加元素到 Hashtable
        hashtable.put(1, "Apple");
        hashtable.put(2, "Banana");
        hashtable.put(3, "Orange");

        // 获取 Hashtable 中的值的枚举
        Enumeration<String> values = hashtable.elements();

        // 遍历枚举并输出值
        while (values.hasMoreElements()) {
            System.out.println(values.nextElement());
        }
    }
}

Hashtable 类的 elements() 方法用于返回此 Hashtable 中的值的枚举(Enumeration)。

LinkedList 是 Java 中的双向链表实现。在 LinkedList 中,每个节点都包含对前一个节点和后一个节点的引用,这使得在链表中插入和删除元素的操作更加高效,因为它不需要像数组那样移动其他元素来保持顺序。

以下是 LinkedList 的基本特点:

  1. 双向链表结构:每个节点包含两个引用,分别指向前一个节点和后一个节点。
  2. 无需连续内存空间:与数组不同,LinkedList 中的节点在内存中可以不必连续存储。
  3. 插入和删除操作高效:由于双向链表的结构,插入和删除操作的时间复杂度为 O(1)。
  4. 随机访问效率低:由于 LinkedList 没有像数组那样可以通过索引进行快速随机访问,因此访问特定位置的元素需要遍历链表,时间复杂度为 O(n)。
  5. 不适合大量数据:由于每个节点都需要额外的空间存储指向前后节点的引用,因此在存储大量数据时,LinkedList 的空间开销会比较大。
代码语言:javascript
复制
import java.util.LinkedList;

public class LinkedListExample {
    public static void main(String[] args) {
        // 创建一个 LinkedList
        LinkedList<String> linkedList = new LinkedList<>();

        // 添加元素到 LinkedList
        linkedList.add("Apple");
        linkedList.add("Banana");
        linkedList.add("Orange");

        // 访问元素
        System.out.println("LinkedList: " + linkedList);

        // 获取第一个和最后一个元素
        String firstFruit = linkedList.getFirst();
        String lastFruit = linkedList.getLast();
        System.out.println("First fruit: " + firstFruit);
        System.out.println("Last fruit: " + lastFruit);

        // 添加元素到列表的开头和末尾
        linkedList.addFirst("Grape");
        linkedList.addLast("Watermelon");
        System.out.println("LinkedList after adding first and last elements: " + linkedList);

        // 删除第一个和最后一个元素
        linkedList.removeFirst();
        linkedList.removeLast();
        System.out.println("LinkedList after removing first and last elements: " + linkedList);

        // 获取元素个数
        int size = linkedList.size();
        System.out.println("Size of LinkedList: " + size);

        // 判断是否包含某个元素
        boolean containsBanana = linkedList.contains("Banana");
        System.out.println("LinkedList contains 'Banana': " + containsBanana);
    }
}
代码语言:javascript
复制
import java.util.ArrayList;

public class ArrayListExample {
    public static void main(String[] args) {
        // 创建一个 ArrayList
        ArrayList<String> arrayList = new ArrayList<>();

        // 添加元素到 ArrayList
        arrayList.add("Apple");
        arrayList.add("Banana");
        arrayList.add("Orange");

        // 访问元素
        System.out.println("ArrayList: " + arrayList);

        // 获取指定位置的元素
        String fruit = arrayList.get(1);
        System.out.println("Fruit at index 1: " + fruit);

        // 修改元素
        arrayList.set(0, "Grape");
        System.out.println("Updated ArrayList: " + arrayList);

        // 删除元素
        arrayList.remove(2);
        System.out.println("ArrayList after removing element at index 2: " + arrayList);

        // 获取元素个数
        int size = arrayList.size();
        System.out.println("Size of ArrayList: " + size);

        // 判断是否包含某个元素
        boolean containsBanana = arrayList.contains("Banana");
        System.out.println("ArrayList contains 'Banana': " + containsBanana);
    }
}

ArrayList 适用于随机访问和遍历操作,而 LinkedList 适用于频繁的插入和删除操作

代码语言:javascript
复制
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        // 创建一个数组
        String[] array = {"apple", "banana", "orange"};

        // 使用 asList 方法将数组转换为列表
        List<String> list = Arrays.asList(array);

        // 修改原数组或列表中的元素
        array[0] = "grape";
        list.set(1, "watermelon");

        // 打印数组和列表
        System.out.println("Array: " + Arrays.toString(array));
        System.out.println("List: " + list);
    }
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-02-23,如有侵权请联系 cloudcommunity@tencent.com 删除

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 创建目录
  • 创建目录及子目录
  • 创建一个或多个(用空格分开即可)
  • 复制文件
  • 复制文件并改名
  • 移动目录到另一个目录
  • 移动目录到另一个目录并改名
  • 强制删除一个目录
  • 文件夹授予权限
  • 解压tar.gz
  • 解压zip
  • 查询目录路径
  • 查看文件
  • 编辑文件
  • 将xxx写入文件
  • 输出文件尾部内容
  • 查看文件
  • 重启网络
  • 关闭防火墙
  • 重启防火墙
  • 启动防火墙
  • 防火墙状态
  • 开启开机自启动
  • 关闭开机自启动
  • 查看已开放端口
  • 开放端口(永久有效)(需要重新加载防火墙)
  • 重新加载防火墙
  • 查看服务开机启动状态
  • 关闭指定服务自启动
  • 开启指定服务自启动
  • 查看磁盘
  • 查看端口
  • 进程启动情况
  • 查看端口占用
  • 查看进程
  • 终止进程
  • 强制终止进程
  • 查看cpu情况
  • 切换账号
  • 新增账号
  • 添加到分组
  • 设置密码
  • 删除用户
  • 登录用户信息
  • 查看内核
  • 启动docker
  • 查看docker状态
  • 重启docker
  • 查看版本
  • 查看信息
  • 获取帮助
  • 查看镜像
  • 启动镜像
  • 查看日志
  • 搜索镜像
  • 打包镜像
  • 删除镜像
  • 进入容器
  • 重启容器
  • 列出容器
  • 停止容器
  • 删除容器
  • 强制停止容器
  • 查看容器内部细节
  • 查看所有卷情况
  • 查看某个卷
  • 构建镜像
  • 构建镜像
  • 构建镜像(不带缓存构建)
  • 查看docker镜像
  • 启动所有镜像
  • 查看所有编排容器(包括已停止容器)
  • 进入指定容器
  • 停止所有启动容器
  • 停止所有启动容器并删除
  • 停止某一个容器
  • 启动某一个容器
  • 重启某一个容器
  • 删除所有容器
  • 查看容器日志
  • 查看容器运行进程
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档