一、介绍一个Java的内存模型(JMM)
JMM是专门解决多线程并发问题下的一套规则。主要解决的问题有原子性,可见性和有序性。
- **原子性:**操作不可分割,要么同时成功,要么同时失败。不能有其他线程插入。通过加锁和CAS(配合volatile)实现
- **可见性:**线程A修改了共享变量的值,线程B也能立马看到。通过volatile关键字和加锁实现。
- 有序性: 编译器或者CPU为了提速会在不影响单线程结果的前提下修改指令顺序,也就是说代码执行顺序和你写的不一样!在单线程情况下不会出错,但在多线程情况下数据会发生错误。有序性就是防止指令重新排序导致的逻辑错误。通过volatile关键字和加锁实现。
JMM把内存抽象为以下两类:
- **主内存(Main Memory) :**所有线程共享,存放实例对象、静态变量、数组等共享数据。
- 工作内存(Working Memory): 每个线程私有,线程操作变量时,会先把需要用到的数据从主内存数据拷贝到自己的工作内存,操作完再写回主内存。
二、Java多线程是什么?需要注意什么?
Java多线程是指在一个程序中同时运行多个线程,所有线程共享程序的内存空间,但每个线程都有各自的栈和程序计数器。
需要注意:
**1.线程安全问题:**多个线程同时对一个共享变量进行操作是可能会出现数据错误.。主要是原子性、可见性和有序性。
**2.线程的销毁与创建成本:**线程的不断销毁和创建会浪费系统资源,到时性能降低。推荐使用线程池来管理线程。
除此之外还有例如死锁,线程间通讯,停止线程等就不一一罗列。
三、Java里面的线程和操作系统的线程一样吗?
本质上是一样的。
四、线程的创建方式有哪些?
1.继承Thread类
继承Thread,重写run方法,创建对象,调用start()方法;
Java是单继承的,继承了Thread就无法继承其他类
java
// 自定义线程类,继承 Thread
class MyThread extends Thread {
// 重写 run 方法,定义线程执行的任务
@Override
public void run() {
System.out.println("线程 " + Thread.currentThread().getName() + " 正在运行");
}
}
public class Main {
public static void main(String[] args) {
// 创建线程实例
MyThread thread1 = new MyThread();
MyThread thread2 = new MyThread();
// 启动线程
thread1.start();
thread2.start();
}
}
2.实现Runnable接口
实现Runnable接口,重写run方法,创建Runnable实例,传入Thread构造,调用start方法;
避免了Java的单继承机制。
java
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("线程正在执行,实现方式:实现Runnable接口");
}
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start();
}
}
3.实现Callable接口
搭配FutureTask使用。重写call方法,创建Callble实例,将Callable包装为FutureTask,传入Thread构造,调用start方法。
避免了Java的单继承机制。
java
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
import java.util.concurrent.ExecutionException;
public class CallableWithFutureTaskExample {
// 定义一个实现Callable接口的任务
static class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
// 模拟耗时操作
Thread.sleep(1000);
return "Callable任务执行完成";
}
}
public static void main(String[] args) {
// 创建Callable实例
MyCallable callable = new MyCallable();
// 将Callable包装为FutureTask
FutureTask<String> futureTask = new FutureTask<>(callable);
// 启动线程执行任务
Thread thread = new Thread(futureTask);
thread.start();
try {
// 获取任务执行结果(阻塞直到任务完成)
String result = futureTask.get();
System.out.println("任务结果: " + result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
4.使用线程池(实际开发最常用)
java
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolExample {
public static void main(String[] args) {
// 创建固定大小的线程池(实际开发推荐使用自定义的ThreadPoolExecutor)
ExecutorService executor = Executors.newFixedThreadPool(5);
// 提交10个任务到线程池
for (int i = 0; i < 10; i++) {
final int taskId = i;
executor.execute(() -> {
System.out.println("Task " + taskId + " is running on " + Thread.currentThread().getName());
try {
Thread.sleep(1000); // 模拟任务耗时
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
// 优雅关闭线程池
executor.shutdown();
}
}
五、如何停止一个线程?
1. 最佳方案:自定义 volatile 标志位
class MyThread extends Thread {
// 必须 volatile 保证可见性
private volatile boolean stop = false;@Override
public void run() {
while (!stop) {
// 正常业务逻辑
}
System.out.println("线程安全退出");
}// 对外提供停止方法
public void stopThread() {
stop = true;
}
}MyThread t = new MyThread();
t.start();t.stopThread(); // 温和停止
2. 官方推荐:使用 interrupt() 中断机制
Thread t = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
try {
// 执行业务
Thread.sleep(1000);
} catch (InterruptedException e) {
// 收到中断信号,退出循环
break;
}
}
});
t.start();t.interrupt(); // 发送中断信号
3.线程池中停止
shutdown()
shutdownNow()(底层调用了interrupt)