Java线程安全体现在三方面:
- 原子性 :提供互斥访问,同一时刻只能有一个线程对数据进行操作,在Java中使用了atomic包和synchronized关键字来确保原子性
- 可见性:一个线程对主内存的修改可以及时地被其他线程看到,在Java中使用volatile关键字确保可见性
- 有序性:由于指令重排,线程中的代码执行可能是无需的,Java通过happens-before原则来确保有序性
线程创建方式:
1、继承Thread类并重写run方法
- 优点:编写简单,访问当前线程时可直接使用this获取
- 缺点:无法再继承其他父类
2、实现Runnable接口并重写run方法,后作为参数传递给Thread类
- 优点:可以继承其他父类
- 缺点:需要使用Thread.currentThread()方法获取当前线程
3、实现Callable接口创建FutureTask,FutureTask作为参数传递给Thread类
java
class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
// 线程执行的代码,这里返回一个整型结果
return 1;
}
}
public static void main(String[] args) {
MyCallable task = new MyCallable();
FutureTask<Integer> futureTask = new FutureTask<>(task);
Thread t = new Thread(futureTask);
t.start();
try {
Integer result = futureTask.get(); // 获取线程执行结果
System.out.println("Result: " + result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
- 优点:可以调用futureTask,get()方法获取返回值,可以继承多个父类
- 缺点:编程复杂
Java线程状态有哪些?
线程状态 | 解释 |
---|---|
NEW | 尚未启动的线程状态,即线程创建,还未调用start方法 |
RUNNABLE | 就绪状态 (调用start,等待调度)+正在运行 |
BLOCKED | 等待监视器锁时,陷入阻塞状态 |
WAITING | 调用wait方法进入等待,等待状态的线程正在等待另一线程执行特定的操作(如notify) |
TIMED_WAITING | 调用sleep方法,具有指定等待时间的等待状态 |
TERMINATED | 线程完成执行,终止状态 |
sleep和wait方法的区别:
特性 | sleep() |
wait() |
---|---|---|
所属类 | Thread 类(静态方法) |
Object 类(实例方法) |
锁释放 | ❌ | ✅ |
使用前提 | 任意位置调用 | 必须在同步块内(持有锁) |
唤醒机制 | 超时自动恢复(Timed_Waiting) | 需 notify() /notifyAll() 或超时 |
设计用途 | 暂停线程执行,不涉及锁协作 | 线程间协调,释放锁让其他线程工作 |
调用sleep方法会主动让出CPU时间片,但不会释放线程已持有的锁,可能导致其他竞争线程阻塞。
blocked和waiting状态的区别:
| 特性 | blocked | waiting |
| 触发条件 | 锁竞争失败,被动触发阻塞直到锁可用 | 主动调用wait、join或park方法 |
唤醒机制 | 锁释放时自动唤醒 | 调用Object.notyfi()/notifyAll()唤醒 |
---|
notify和notifyAll的区别?
- notify:唤醒一个线程 (具体策略由jvm实现,例如hotspot是以先进先出的顺序唤醒 ),其他线程依然处于等待状态 ,若唤醒的线程没有继续调用notify ,则其他线程只能等待超时或被中断
- notifyAll:所有线程退出待唤醒状态,开始竞争锁
线程通信(volatile与synchronized区别)
使用volatile修饰,使所有线程可即时察觉到变量的变化
- 写入时会将变量直接写入到主内存中
- 读取时会直接前往主内存读取变量
- 通过绕过工作内存实现
- 优点:避免变量暂存到线程独享的工作内存中,或一直读取工作内存中的数据
- 缺点:无法确保写入操作的原子性,不能确保并发安全
使用synchronized
- 线程进入同步代码块 时,清空工作内存,重新在主内存中加载变量数据
- 线程离开同步代码块 时,将工作内存的变量强制刷新到主内存中
- 通过Monitor实现
- 优点:确保原子性和变量的可见性
- 缺点:依赖操作系统底层互斥和线程上下文切换,性能开销较大
线程中断方法:
| 方法 | 适用场景 | 注意事项 |
| 循环检测标志位 | 简单无阻塞的逻辑 | 确保标志位使用 volatile
或通过锁保证可见性 |
| 中断机制 | 可中断的阻塞操作 | 正确处理 InterruptedException
并恢复中断标志 |
| Future.cancel() | 线程池管理任务 | 需要线程池任务支持中断处理机制 |
资源关闭 | 不可中断的阻塞操作(如Sockets) | 显式关闭资源触发异常,结合中断状态判断回滚 |
---|
自定义标志位:
java
public class SafeStopWithFlag implements Runnable {
// 使用 volatile 保证可见性
private volatile boolean running = true;
@Override
public void run() {
while (running) {
// 处理任务逻辑
System.out.println("Thread is running...");
Thread.sleep(1000)
}
System.out.println("Thread terminated safely.");
}
// 停止线程的方法(由外部调用)
public void stop() {
running = false;
}
}
线程中断机制:
java
public class InterruptExample implements Runnable {
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
System.out.println("Working...");
Thread.sleep(1000);
} catch (InterruptedException e) {
// 当阻塞时被中断,抛出异常并清除中断状态
System.out.println("Interrupted during sleep!");
Thread.currentThread().interrupt(); // 重新设置中断标志
}
}
System.out.println("Thread terminated by interrupt.");
}
}
Future取消任务:
java
public class FutureCancelDemo {
public static void main(String[] args) {
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<?> future = executor.submit(() -> {
while (!Thread.currentThread().isInterrupted()) {
System.out.println("Task running...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("Task interrupted.");
Thread.currentThread().interrupt();
}
}
});
try {
Thread.sleep(3000);
future.cancel(true); // true表示尝试中断任务线程
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
executor.shutdown();
}
}
}
遇到同步操作或I/O连接的不可中断阻塞操作:
java
public class SocketHandler implements Runnable {
private ServerSocket serverSocket;
public SocketHandler(ServerSocket serverSocket) {
this.serverSocket = serverSocket;
}
@Override
public void run() {
try {
// serverSocket.accept()阻塞时无法响应中断
while (!Thread.currentThread().isInterrupted()) {
Socket socket = serverSocket.accept();
// 处理连接...
}
} catch (IOException e) {
if (Thread.currentThread().isInterrupted()) {
System.out.println("Thread stopped by interrupt.");
}
}
}
// 特殊关闭方法(销毁资源)
public void stop() {
try {
serverSocket.close(); // 关闭资源使accept()抛出异常
} catch (IOException e) {
System.out.println("Error closing socket: " + e);
}
}
}