卡码笔记
首页
计算机基础
C++
Java
面经
笔记广场 (opens new window)
代码随想录 (opens new window)
首页
计算机基础
C++
Java
面经
笔记广场 (opens new window)
代码随想录 (opens new window)
  • 基础与面向对象

  • 集合

  • 异常

  • 字符串

  • JVM

  • 并发与多线程

    • Java创建线程有几种方式?
      • 简要回答
      • 详细回答
      • 使用场景
      • 知识图解
      • 知识扩展
    • 线程start和run的区别?
    • Java线程安全的实现?
    • synchronized和lock、reentrantlock的区别是什么?
    • 说一说你对synchronized的理解?
    • volatile关键字的作用有哪些?
    • volatile与synchronized的对比?
    • 你知道Java中有哪些锁吗?
    • 为什么要有线程池,线程太多会怎样?
    • 说一说线程池的常用参数
    • BIO、NIO、AIO的区别
  • JDK

  • Spring

  • 设计模式

# 线程创建方式

# 简要回答

在Java中,创建线程有四种方式:

  • 继承Thread类,重写run()方法。
  • 实现Runnable接口,实现run()方法。
  • 使用线程池(Executor框架)。
  • 实现Callable接口(配合Future/FutureTask),实现call()方法。

# 详细回答

  1. 继承Thread类:
  • 用户可以通过继承Thread类创建一个新的线程,并且重写run()方法,该方法包含线程的执行代码。
  • 创建该类的实例,调用start()方法启动线程。
class MyThread extends Thread{
  @Override
  public void run(){
    // 线程执行的逻辑代码
    System.out.println("MyThread is running...");
  }
}

//创建并启动线程
MyThread myThread = new MyThread();
myThread.start();
1
2
3
4
5
6
7
8
9
10
11
  • 优点:代码简单,如果需要访问当前线程,无需使用Thread.currentThread()方法,直接使用this即可获取当前线程。
  • 缺点:Java只支持单继承,线程类不能再继承其他的父类
  1. 实现Runnable接口:
  • 用户可以实现Runnable接口,实现run()方法。
  • 创建一个Thread对象,将实现类Runnable接口的类的实例作为参数传递给Thread构造函数,调用Thread对象的start()方法启动线程。
class MyRunnable implements Runnable{
  @Override
  public void run(){
    //线程执行的逻辑代码
    System.out.println("MyRunnable is running...");
  }
}

// 创建并启动线程
Thread myThread = new Thread(new Runnable());
myThread.start();
1
2
3
4
5
6
7
8
9
10
11
  • 优点:可以实现多个线程共享同一个目标对象,方便实现资源的并发控制,且线程的创建和业务逻辑分离。
  • 缺点:如果需要访问当前线程,需要使用Thread.currentThread()方法,run()方法没有返回值。
  1. 使用Executer框架:
  • Executor框架是Java并发编程中的高级工具,通过Executer,可以将任务提交给线程池,由线程池来管理线程的生命周期和执行。
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

class MyTask implements Runnable{
  @Override
  public void run(){
    //线程执行的逻辑代码
    System.out.println("MyTask is running...");
  }
}

// 创建线程池并提交任务
Executor executor = Executors.newFixedThreadPool(3);
// 提交任务
executor.execute(new MyTask());
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  • 优点:可以避免频繁创建和销毁线程的开销,可以控制线程并发数量,监控线程状态,可以接受多种任务类型(Runnable和Callable)。
  • 缺点:核心线程占用系统资源,线程池配置比较复杂,容易造成资源浪费。
  1. 实现Callable接口:
  • Callable的call()方法可以有返回值并且可以抛出异常。
  • Callable的返回值在子线程中产生,需要配合Future/FutureTask用于获取线程执行的结果。
  • 可以使用线程池配合future,将MyCallable实例在线程池执行。或者由于Thread只能执行Runnable任务,可以使用FutureTask包装Callable任务。
// 使用Future和线程池
class MyCallable implements Callable<Integer>{
  publc Integer call() throws Exeception{
    //线程执行的代码,返回一个整形结果
    return 42;
  }
}

ExecutorService excutor = Executors.newSingleThreadExecutor();
Future<Integer> future = excutor.submit(new MyCallable());

try{
  //获取线程执行结果
  Integer result = future.get();
}catch(InterruptedExeception | ExecutionException e){
  //处理异常
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 使用FutureTask包装
// MyCallable代码同上

MyCallable task = new MyCallable();
FutureTask<Integer> futureTask = new FutureTask<>(task);
Thread myThread = new Thread(futureTask);
myThread.start();

try{
  //获取线程执行结果
  Integer result = futureTask.get();
}catch(InterruptedExeception | ExecutionException e){
  //处理异常
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  • 优点:Callable接口的方法有返回值,还可以抛出异常
  • 缺点:代码复杂,访问当前线程需要调用Thread.currentThread()方法。

# 使用场景

创建方法 适用场景
继承Thread类 简单场景
实现Runnable接口 多线程共享资源场景
实现Callable接口 需要任务返回结果或处理异常的场景
使用线程池(Executor) 生产环境,高并发,需要复用线程场景

# 知识图解

1. 创建线程方法示意图

2. 继承Thread方法和实现Runnable方法示意图

3. 实现Callable接口示意图

# 知识扩展

  1. 扩展:
  • 线程池的核心参数: 线程池的构造函数有七个参数
    • corePoolSize 核心线程数: 线程池中长期存活的线程数
      • 如果线程池中线程数量少于corePoolSize,这些线程处于空闲状态也不会被销毁
    • maximumPoolSize 最大线程数: 线程池允许创建的最大线程数(包括核心线程和非核心线程)
      • 当核心线程已满,队列已满时,如果当前线程小于最大线程数,就会创建新的线程执行此任务,否则触发拒绝策略
    • keepAliveTime 空闲线程存活时间: 当线程数大于corePoolSize时,如果某个线程的空闲时间超过了keepAliveTime,那么这个线程就会被销毁
    • TimeUnit 与keepAliveTime一起使用,指定keepAliveTime的时间单位
    • workQueue 线程池任务队列: 线程池存放任务的队列,没有空闲线程执行新任务时,用来存储线程池的所有待执行任务
    • ThreadFactory 创建线程的工厂: 线程池创建线程时调用的工厂方法,通过此方法可以设置线程的优先级,线程命名规则以及线程类型(用户线程还是守护线程)
    • RejectedExecutionHandler 拒绝策略: 当线程池的任务超出线程池队列可以存储的最大值之后,执行的策略
  1. 面试官可能追问:
  • Q1:调用start()方法和直接调用run()方法有什么区别?
    • start()本质是native方法,会启动一个新线程,由JVM调用run()方法,实现真正的并发执行。而直接调用run()是仅作为普通方法在当前线程执行,不会创建新线程。
  • Q2:线程池创建线程和手动创建线程有什么不同?
    • 手动创建线程是即用即建,线程池能够实现预先创建和线程复用,能够减少线程创建/销毁开销。
  • Q3:Future的get()方法阻塞怎么避免主线程的长时间等待?
    • 使用get(long timeout, TimeUnit unit) 设置超时时间,避免无限阻塞。
    • 用isDone() 先判断任务是否完成,再调用get()。
Last Updated: 3/10/2026, 6:08:48 PM

← 常见的垃圾回收器有哪些? 线程start和run的区别? →

评论

验证登录状态...

侧边栏
夜间
卡码简历
代码随想录
卡码投递表🔥
2026群
添加客服微信 PS:通过微信后,请发送姓名-学校-年级-2026实习/校招
支持卡码笔记
鼓励/支持/赞赏Carl
1. 如果感觉本站对你很有帮助,也可以请Carl喝杯奶茶,金额大小不重要,心意已经收下
2. 希望大家都能梦想成真,有好的前程,加油💪