- 进程是资源分配的最小单位,而线程是 CPU 调度的最小单位;
- 线程是进程划分成的更小的运行单位,一个进程在其执行的过程中可以产生多个线程。同一个进程中的多个线程之间能够并发执行。
- 不同进程地址空间相互独立,同一进程内的线程共享同一地址空间。一个进程的线程在另一个进程内是不可见的;
- 线程执行开销小,但不利于资源的管理和保护;而进程则相反。
- 在Java中,多个线程共享进程的堆和方法区 (JDK1.8 之后的元空间)资源,但是每个线程有自己的程序计数器、虚拟机栈 和 本地方法栈。
- 实现 Runnable 接口;
- 实现 Callable 接口;
- 继承 Thread 类。
- 线程池创建。
实现 Runnable 接口
- 需要实现 run() 方法。
- 通过 Thread 调用 start() 方法来启动线程。
public class MyRunnable implements Runnable {
public void run() {
// ...
}
}
public static void main(String[] args) {
MyRunnable instance = new MyRunnable();
Thread thread = new Thread(instance);
thread.start();
}
实现 Callable 接口
- Callable 可以有返回值,返回值通过 FutureTask 进行封装。
public class MyCallable implements Callable {
public Integer call() {
return 123;
}
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyCallable mc = new MyCallable();
FutureTask ft = new FutureTask<>(mc);
Thread thread = new Thread(ft);
thread.start();
System.out.println(ft.get());
}
继承 Thread 类
- 同样也是需要实现 run() 方法,因为 Thread 类也实现了 Runable 接口。
- 当调用 start() 方法启动一个线程时,虚拟机会将该线程放入就绪队列中等待被调度,当一个线程被调度时会执行该线程的 run() 方法。
public class MyThread extends Thread {
public void run() {
// ...
}
}
public static void main(String[] args) {
MyThread mt = new MyThread();
mt.start();
}
Runnable 和 Callable 有什么区别?
- Runnable 接口中的 run() 方法的返回值是 void,它做的事情就是去执行 run() 方法中的代码;
- Callable 接口中的 call() 方法是有返回值的,和 FutureTask 配合(不会 FutureTask 就不说)可以用来获取执行的结果,也可以抛出异常。
谈谈 synchronized 和 ReentrantLock 的区别
- synchronized 是一个关键字,ReentrantLock 是类
- synchronized 依赖于 JVM ,而 ReenTrantLock 依赖于 JDK
- 相比synchronized,ReentrantLock 更加灵活:
- 它支持等待可中断,也就是说正在等待的线程可以选择放弃等待,改为处理其他事情。
- 可以实现公平锁,ReentrantLock 可以指定是公平锁还是非公平锁,而 synchronized 只能是非公平锁
- 可实现选择性通知,在一个 Lock 对象中可以创建多个 Condition 实例,线程对象可以注册在指定的 Condition 中,从而可以有选择性的进行线程通知
- 同样也是需要实现 run() 方法,因为 Thread 类也实现了 Runable 接口。
- 当调用 start() 方法启动一个线程时,虚拟机会将该线程放入就绪队列中等待被调度,当一个线程被调度时会执行该线程的 run() 方法。
public class MyThread extends Thread {
public void run() {
// ...
}
}
public static void main(String[] args) {
MyThread mt = new MyThread();
mt.start();
}
Runnable 和 Callable 有什么区别?
- Runnable 接口中的 run() 方法的返回值是 void,它做的事情就是去执行 run() 方法中的代码;
- Callable 接口中的 call() 方法是有返回值的,和 FutureTask 配合(不会 FutureTask 就不说)可以用来获取执行的结果,也可以抛出异常。
- synchronized 是一个关键字,ReentrantLock 是类
- synchronized 依赖于 JVM ,而 ReenTrantLock 依赖于 JDK
- 相比synchronized,ReentrantLock 更加灵活:
- 它支持等待可中断,也就是说正在等待的线程可以选择放弃等待,改为处理其他事情。
- 可以实现公平锁,ReentrantLock 可以指定是公平锁还是非公平锁,而 synchronized 只能是非公平锁
- 可实现选择性通知,在一个 Lock 对象中可以创建多个 Condition 实例,线程对象可以注册在指定的 Condition 中,从而可以有选择性的进行线程通知
synchronized 关键字就相当于整个 Lock 对象中只有一个 Condition 实例,所有的线程都注册在它一个身上。如果执行 notifyAll()方法的话就会通知所有处于等待状态的线程
而 Condition 实例的 signalAll()方法 只会唤醒注册在该 Condition 实例中的所有等待线程。
说下对线程池的理解?为什么要使用线程池?线程池(ThreadPool)是一种基于池化思想来管理和使用线程,它是将多个线程预先存储在一个“池子”内,当有任务出现时,只需要从 线程池 内取出相应的线程执行对应的任务即可。可以避免重新创建和销毁线程所带来性能开销。
使用线程池的优点:
- 降低资源消耗(通过重复利用已创建的线程降低线程创建和销毁造成的消耗。)
- 提高响应速度(任务可以不需要等到线程创建就能立即执行。)
- 便于统一管理线程对象
方式一:通过 ThreadPoolExecutor 的构造方法实现:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
// 省略相关代码
}
方式二:通过工具类 Executors 来实现
为什么线程池不允许使用Executors去创建? 推荐方式是什么?推荐通过 ThreadPoolExecutor 的方式去创建。
Executors 创建线程池对象的弊端如下:
- FixedThreadPool 和 SingleThreadExecutor :允许请求的队列长度为 Integer.MAX_VALUE,可能堆积大量的请求,从而导致 OOM。
- CachedThreadPool 和 ScheduledThreadPool :允许创建的线程数量为Integer.MAX_VALUE ,可能会创建大量线程,从而导致 OOM。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
// 省略相关代码
}
corePoolSize:核心线程数 ,定义了最小可以同时运行的线程数量。
maximumPoolSize:最大线程数,如果队列满了,并且已创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务。
keepAliveTime:线程空闲时的存活时间,就是线程池中除了核心线程之外的其他线程最长可以存活的时间。
unit:keepAliveTime的单位
workQueue:任务队列,用于保存等待执行的任务的阻塞队列。
threadFactory:创建线程的工厂,主要用来创建线程。
handler:拒绝策略,当任务队列满了,且没有空闲的工作线程,如果继续提交任务,必须采取一种策略处理该任务。有四种策略:
- AbortPolicy:抛出 RejectedExecutionException 异常来拒绝新任务的处理。
- CallerRunsPolicy:用调用者所在的线程来执行任务。
- DisCardPolicy:直接丢弃任务。
- DisCardOldSetPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务



