目录
一、Java线程核心机制
🔥 问题3:start()与run()的底层执行差异
线程启动流程图解
核心差异对照表
方法 | 执行线程 | 调用次数 | JVM操作 | 典型应用场景 |
---|---|---|---|---|
start() | 新创建的子线程 | 单次 | 触发native线程创建 | 多线程任务启动 |
run() | 主调用线程 | 多次 | 普通Java方法调用 | 单线程测试/调试 |
代码验证示例
public class StartVsRun {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
System.out.println("当前线程: " + Thread.currentThread().getName());
});
thread.start(); // 输出: Thread-0
thread.run(); // 输出: main
}
}
🔥 问题4:Thread与Runnable的六大维度对比
类关系UML图
维度 | Thread | Runnable |
---|---|---|
类型 | 具体类 | 接口 |
继承限制 | 占用继承名额 | 不占用 |
资源共享 | 实例变量独立 | 可共享同一实例 |
扩展性 | 单继承限制 | 支持多实现 |
线程池兼容性 | 需包装为Runnable | 直接支持 |
设计模式 | 具体实现 | 策略模式 |
最佳实践代码
// 推荐实现方式
public class DownloadTask implements Runnable {
private String url;
public DownloadTask(String url) {
this.url = url;
}
@Override
public void run() {
// 文件下载逻辑
}
}
// 使用示例
ExecutorService pool = Executors.newFixedThreadPool(5);
pool.execute(new DownloadTask("https://example.com/file1"));
pool.execute(new DownloadTask("https://example.com/file2"));
🔥 问题5:线程参数传递三大方案
参数传递方案对比
方式 | 实现示例 | 优点 | 缺点 |
---|---|---|---|
构造器传参 | new MyThread(param).start() |
线程安全 | 创建后参数不可变 |
成员变量传参 | thread.setConfig(config) |
灵活修改 | 需处理线程可见性问题 |
回调函数传参 | executor.submit(() -> task(param)) |
Lambda简洁 | 要求参数final或等效 |
Lambda传参最佳实践
public class ThreadParamDemo {
public static void main(String[] args) {
String config = "server.properties";
// Lambda传参(实质是闭包)
new Thread(() -> loadConfig(config)).start();
// 通过Future传参
ExecutorService pool = Executors.newSingleThreadExecutor();
Future<String> future = pool.submit(() -> processData(config));
}
private static void loadConfig(String path) {
// 配置文件加载逻辑
}
private static String processData(String input) {
// 数据处理逻辑
return "processed_" + input;
}
}
二、Spring依赖注入全景解析
🌟 Spring依赖注入四大方式
DI实现方式对比表
注入方式 | 实现示例 | 优点 | 缺点 |
---|---|---|---|
构造器注入 | new Service(dao) |
不可变/线程安全 | 参数多时代码冗长 |
Setter注入 | service.setDao(dao) |
灵活可选依赖 | 可能破坏不变性 |
字段注入 | @Autowired private Dao dao |
代码简洁 | 测试困难/隐藏依赖 |
方法注入 | @Autowired public void init(Dao dao) |
精确控制时机 | 使用频率较低 |
构造器注入最佳实践
@Service
public class OrderService {
private final PaymentService paymentService;
private final InventoryService inventoryService;
@Autowired
public OrderService(PaymentService paymentService,
InventoryService inventoryService) {
this.paymentService = paymentService;
this.inventoryService = inventoryService;
}
}
🌟 Spring自动装配原理图解
自动装配注解对比
注解 | 装配方式 | 适用场景 | 冲突解决 |
---|---|---|---|
@Autowired | 按类型优先 | 多数单实现场景 | @Qualifier指定名称 |
@Resource | 按名称优先 | JNDI资源注入 | 名称不存在时按类型 |
@Inject | 与@Autowired类似 | JSR-330标准实现 | 需要额外依赖 |
三、高频面试题强化训练
1. Spring为什么推荐构造器注入?
不可变性:确保依赖项在初始化后不变
循环依赖检测:启动时立即发现依赖问题
测试友好:便于通过构造器传递Mock对象
线程安全:避免并发修改依赖项
2. @Autowired与@Resource的区别?
维度 | @Autowired | @Resource |
---|---|---|
标准归属 | Spring专属 | JSR-250标准 |
默认装配策略 | 按类型 | 按名称 |
必需性控制 | required=false | 无 |
参数指定方式 | @Qualifier | name属性 |
应用范围 | 字段/构造器/方法 | 字段/setter方法 |
3. 如何解决多个同类型Bean的冲突?
// 方案一:使用@Primary标记主候选
@Bean
@Primary
public DataSource masterDataSource() {
return new HikariDataSource();
}
// 方案二:使用@Qualifier指定名称
@Autowired
@Qualifier("backupDataSource")
private DataSource dataSource;
// 方案三:使用自定义限定注解
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier("report")
public @interface ReportDataSource {}
@Bean
@ReportDataSource
public DataSource reportDataSource() {
return new DruidDataSource();
}
实战建议:
在Spring Boot中通过
@ConfigurationProperties
实现类型安全配置注入使用
@Lazy
注解延迟初始化资源密集型Bean通过
@Profile
实现环境特定的Bean装配
💬 你在项目中更倾向于使用哪种依赖注入方式?遇到过哪些注入难题?
🎁 关注+转发,抽送《阿里Java开发手册》电子书