Java线程安全体现在三方面:
- 原子性:提供互斥访问,同一时刻只能有一个线程对数据进行操作,在Java中使用了atomic包和synchronized关键字来确保原子性
- 可见性:一个线程对主内存的修改可以及时地被其他线程看到,在Java中使用volatile关键字确保可见性
- 有序性:由于指令重排,线程中的代码执行可能是无需的,Java通过happens-before原则来确保有序性
线程创建方式:
1、继承Thread类并重写run方法
- 优点:编写简单,访问当前线程时可直接使用this获取
- 缺点:无法再继承其他父类
2、实现Runnable接口并重写run方法,后作为参数传递给Thread类
- 优点:可以继承其他父类
- 缺点:需要使用Thread.currentThread()方法获取当前线程
3、实现Callable接口创建FutureTask,FutureTask作为参数传递给Thread类
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) |
显式关闭资源触发异常,结合中断状态判断回滚 |
自定义标志位:
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;
}
}
线程中断机制:
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取消任务:
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连接的不可中断阻塞操作:
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);
}
}
}