学习笔记:线程池,反射,网络通信,注解

27 阅读8分钟

一、线程池

1. 代码示例(企业级最佳实践)

java

运行

import java.util.concurrent.*;

/**
 * 线程池企业级使用示例
 * 核心:根据业务场景选择合适的线程池参数,避免使用Executors默认创建方式
 */
public class ThreadPoolExample {
    // 自定义线程池(核心参数根据CPU核心数和业务特性调整)
    private static final ThreadPoolExecutor THREAD_POOL = new ThreadPoolExecutor(
            2, // 核心线程数(CPU核心数)
            4, // 最大线程数(核心线程数*2)
            60L, // 空闲线程存活时间
            TimeUnit.SECONDS, // 时间单位
            new ArrayBlockingQueue<>(100), // 有界队列(避免内存溢出)
            new ThreadFactory() { // 自定义线程工厂(便于排查问题)
                private int count = 0;
                @Override
                public Thread newThread(Runnable r) {
                    Thread thread = new Thread(r);
                    thread.setName("business-thread-" + (++count));
                    return thread;
                }
            },
            new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略(核心线程忙、队列满、最大线程满时,由调用线程执行)
    );

    public static void main(String[] args) {
        try {
            // 提交10个任务到线程池
            for (int i = 0; i < 10; i++) {
                int taskId = i;
                THREAD_POOL.submit(() -> {
                    System.out.println("任务" + taskId + "由线程:" + Thread.currentThread().getName() + "执行");
                    try {
                        // 模拟业务处理
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                });
            }
        } finally {
            // 优雅关闭线程池(企业级必做)
            THREAD_POOL.shutdown();
            try {
                if (!THREAD_POOL.awaitTermination(1, TimeUnit.MINUTES)) {
                    THREAD_POOL.shutdownNow();
                }
            } catch (InterruptedException e) {
                THREAD_POOL.shutdownNow();
            }
        }
    }
}

2. 企业面试题

  1. 基础题:为什么不建议使用 Executors 创建线程池?(答:FixedThreadPool/SingleThreadPool 使用无界队列,可能导致 OOM;CachedThreadPool/ScheduledThreadPool 最大线程数为 Integer.MAX_VALUE,可能创建大量线程导致 OOM)
  2. 进阶题:线程池的核心参数有哪些?核心线程数、最大线程数、存活时间、队列、线程工厂、拒绝策略
  3. 实战题:如何优雅关闭线程池?为什么不能直接用 shutdownNow?(答:shutdown () 是平缓关闭,不再接收新任务,等待已提交任务执行完;shutdownNow () 是强制关闭,中断正在执行的任务,返回未执行的任务列表)
  4. 场景题:如果线程池处理的任务有返回值,应该用 submit 还是 execute?如何获取返回值?(答:用 submit,返回 Future 对象,通过 get () 获取返回值,注意处理 InterruptedException 和 ExecutionException)

3. 企业实际应用

  • 接口异步处理:用户请求接口后,核心逻辑同步返回,非核心逻辑(如日志记录、数据统计、消息推送)提交到线程池异步执行,提升接口响应速度。
  • 批量任务处理:电商平台订单批量处理、数据导出 / 导入、报表生成等耗时任务,通过线程池并行处理提升效率。
  • 中间件底层:Tomcat、Netty 等框架的线程模型核心依赖线程池(如 Tomcat 的 Connector 线程池处理 HTTP 请求)。

二、网络通信(基于 Java Socket)

1. 代码示例(TCP 客户端 - 服务端通信)

服务端(企业级简易版)

java

运行

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class TcpServer {
    public static void main(String[] args) {
        // 绑定8888端口
        try (ServerSocket serverSocket = new ServerSocket(8888)) {
            System.out.println("服务端启动,监听端口8888...");
            // 循环接收客户端连接(实际企业应用会结合线程池处理多客户端)
            while (true) {
                Socket clientSocket = serverSocket.accept();
                System.out.println("客户端" + clientSocket.getInetAddress() + "连接成功");
                // 每个客户端连接分配一个线程处理(实际用线程池)
                new Thread(() -> handleClient(clientSocket)).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 处理客户端请求
    private static void handleClient(Socket clientSocket) {
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
             OutputStreamWriter writer = new OutputStreamWriter(clientSocket.getOutputStream())) {
            // 读取客户端消息
            String msg = reader.readLine();
            System.out.println("收到客户端消息:" + msg);
            // 响应客户端
            writer.write("服务端已收到:" + msg + "\n");
            writer.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                clientSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

客户端

java

运行

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;

public class TcpClient {
    public static void main(String[] args) {
        // 连接服务端
        try (Socket socket = new Socket("localhost", 8888);
             OutputStreamWriter writer = new OutputStreamWriter(socket.getOutputStream());
             BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()))) {
            // 发送消息给服务端
            String msg = "Hello, Server!";
            writer.write(msg + "\n");
            writer.flush();
            // 接收服务端响应
            String response = reader.readLine();
            System.out.println("服务端响应:" + response);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

2. 企业面试题

  1. 基础题:TCP 和 UDP 的区别?(答:TCP 面向连接、可靠、有序、慢;UDP 无连接、不可靠、无序、快)
  2. 进阶题:Socket 通信中,InputStream 的 read () 方法是阻塞的吗?如何处理非阻塞 IO?(答:默认是阻塞的;NIO 通过 Selector、Channel、Buffer 实现非阻塞 IO)
  3. 实战题:为什么实际项目中不用原生 Socket 开发网络通信?(答:原生 Socket 需要处理粘包 / 拆包、重连、心跳、序列化等问题,开发成本高,企业一般用 Netty、MINA 等框架)
  4. 场景题:Netty 的核心组件有哪些?为什么 Netty 比原生 NIO 性能更好?(答:核心组件:Channel、EventLoop、ChannelHandler、Bootstrap;优化点:零拷贝、Reactor 模型、线程模型优化、内存池等)

3. 企业实际应用

  • 即时通讯:IM 聊天工具(如企业微信、钉钉)底层基于 TCP/UDP+Netty 实现。
  • 微服务通信:Dubbo、gRPC 等 RPC 框架底层基于 Netty 实现跨服务网络通信。
  • 中间件交互:Redis、Kafka 等中间件的客户端与服务端通信基于 TCP 协议。

三、反射

1. 代码示例(企业级常用场景)

java

运行

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * 反射企业级应用示例:动态调用方法、操作私有属性
 * 核心:框架开发(如Spring、MyBatis)的基础
 */
public class ReflectionExample {
    // 模拟业务实体类
    static class User {
        private Long id;
        private String name;

        public User() {}

        public User(Long id, String name) {
            this.id = id;
            this.name = name;
        }

        private String getUserName() {
            return "用户名:" + this.name;
        }

        @Override
        public String toString() {
            return "User{id=" + id + ", name='" + name + "'}";
        }
    }

    public static void main(String[] args) throws Exception {
        // 1. 获取Class对象(三种方式)
        Class<User> userClass = User.class;

        // 2. 动态创建对象(无参构造)
        User user1 = userClass.getDeclaredConstructor().newInstance();

        // 3. 操作私有属性
        Field nameField = userClass.getDeclaredField("name");
        nameField.setAccessible(true); // 打破封装(访问私有属性必须)
        nameField.set(user1, "张三");
        Field idField = userClass.getDeclaredField("id");
        idField.setAccessible(true);
        idField.set(user1, 1L);
        System.out.println("动态设置属性后:" + user1);

        // 4. 调用私有方法
        Method getUserNameMethod = userClass.getDeclaredMethod("getUserName");
        getUserNameMethod.setAccessible(true);
        String result = (String) getUserNameMethod.invoke(user1);
        System.out.println("调用私有方法结果:" + result);

        // 5. 通过有参构造创建对象
        Constructor<User> constructor = userClass.getDeclaredConstructor(Long.class, String.class);
        User user2 = constructor.newInstance(2L, "李四");
        System.out.println("有参构造创建对象:" + user2);
    }
}

2. 企业面试题

  1. 基础题:反射的核心作用是什么?获取 Class 对象有哪几种方式?(答:动态获取类信息、调用方法、操作属性,突破封装;方式:类名.class、对象.getClass ()、Class.forName ("全类名"))
  2. 进阶题:反射为什么可以访问私有属性 / 方法?使用反射时需要注意什么?(答:通过 setAccessible (true) 关闭访问检查;注意:性能损耗、破坏封装性、安全风险)
  3. 实战题:Spring IoC 容器是如何利用反射创建 Bean 的?(答:扫描指定包下的类,通过 Class.forName 获取 Class 对象,通过反射创建实例,注入属性)
  4. 场景题:MyBatis 的 Mapper 接口为什么没有实现类却能执行 SQL?(答:MyBatis 通过 JDK 动态代理 + 反射,生成 Mapper 接口的代理类,代理类中通过反射调用对应的 SQL 执行逻辑)

3. 企业实际应用

  • 框架核心:Spring IoC/DI、MyBatis、Hibernate 等框架的底层核心依赖反射。
  • 动态配置:配置文件(如 XML、YAML)指定类名和方法名,程序启动时通过反射动态加载类、调用方法。
  • 注解解析:自定义注解 + 反射实现业务逻辑(如下文注解示例)。

四、注解

1. 代码示例(自定义注解 + 反射解析)

java

运行

import java.lang.annotation.*;
import java.lang.reflect.Method;

/**
 * 企业级自定义注解示例:模拟接口权限校验注解
 */
// 1. 定义自定义注解
@Target(ElementType.METHOD) // 注解作用在方法上
@Retention(RetentionPolicy.RUNTIME) // 运行时保留(反射可获取)
@Documented // 生成文档时包含该注解
public @interface PermissionCheck {
    // 注解属性(权限编码)
    String value() default "";
}

// 2. 使用注解
class UserController {
    @PermissionCheck("user:query")
    public void queryUser() {
        System.out.println("查询用户列表成功");
    }

    @PermissionCheck("user:delete")
    public void deleteUser() {
        System.out.println("删除用户成功");
    }

    public void login() {
        System.out.println("登录成功(无需权限校验)");
    }
}

// 3. 反射解析注解(模拟权限校验逻辑)
public class AnnotationExample {
    public static void main(String[] args) throws Exception {
        UserController controller = new UserController();
        Class<UserController> clazz = UserController.class;

        // 模拟用户拥有的权限
        String userPermission = "user:query";

        // 获取所有方法,解析注解
        for (Method method : clazz.getDeclaredMethods()) {
            // 判断方法是否有PermissionCheck注解
            if (method.isAnnotationPresent(PermissionCheck.class)) {
                PermissionCheck annotation = method.getAnnotation(PermissionCheck.class);
                String requiredPermission = annotation.value();
                // 权限校验
                if (userPermission.equals(requiredPermission)) {
                    // 有权限,调用方法
                    method.invoke(controller);
                } else {
                    System.out.println("调用方法" + method.getName() + "失败:无" + requiredPermission + "权限");
                }
            } else {
                // 无注解,直接调用
                method.invoke(controller);
            }
        }
    }
}

2. 企业面试题

  1. 基础题:注解的元注解有哪些?分别起什么作用?(答:@Target:指定注解作用位置;@Retention:指定注解保留阶段;@Documented:生成文档;@Inherited:允许子类继承父类注解)
  2. 进阶题:注解的 RetentionPolicy 有哪几种?分别适用于什么场景?(答:SOURCE:源码阶段(如 @Override);CLASS:编译阶段(如 Lombok);RUNTIME:运行时(反射解析,如 Spring 注解))
  3. 实战题:如何自定义一个注解,并通过反射解析它?(答:定义注解→添加元注解→在目标类 / 方法上使用→通过反射获取注解信息并处理)
  4. 场景题:Spring 中的 @Autowired、@Service、@RequestMapping 注解的实现原理是什么?(答:Spring 启动时扫描注解,通过反射解析注解信息,完成依赖注入、Bean 注册、请求映射等逻辑)

3. 企业实际应用

  • 权限控制:如 Spring Security 的 @PreAuthorize、自定义权限注解。
  • 接口文档:Swagger 的 @Api、@ApiOperation 注解,自动生成接口文档。
  • ORM 映射:JPA 的 @Entity、@Table、@Column 注解,实现实体类与数据库表的映射。
  • AOP 切面:Spring 的 @Transactional、@Log 注解,结合 AOP 实现事务管理、日志记录。

总结

  1. 线程池:核心是合理配置参数 + 优雅关闭,企业中用于异步处理、批量任务,避免 OOM 和线程泄露。
  2. 网络通信:原生 Socket 仅作基础学习,企业实际用 Netty 等框架解决粘包、性能问题,用于 RPC、IM 等场景。
  3. 反射:框架开发的核心,可动态操作类 / 方法 / 属性,注意性能和封装性问题。
  4. 注解:结合反射实现无侵入式扩展,企业中用于权限、配置、文档、AOP 等场景。