Java—多线程

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);
        }
    }
}
相关推荐
vvilkim3 分钟前
C# 数组与字符串:全面解析与应用实践
开发语言·算法·c#
QQ_hoverer9 分钟前
Java设计模式之工厂模式与策略模式简单案例学习
java·开发语言·学习·设计模式·策略模式
Ashlee_code12 分钟前
TRS收益互换平台开发实践:从需求分析到系统实现
java·数据结构·c++·python·架构·php·需求分析
我的青春不太冷15 分钟前
Android高级开发第二篇 - JNI 参数传递与 Java → C → Java 双向调用
android·java·c语言
master-dragon20 分钟前
设计模式-迭代器模式
java·设计模式·迭代器模式
观无26 分钟前
若依微服务的定制化服务
java·开发语言
我的golang之路果然有问题31 分钟前
快速了解 GO之接口解耦
开发语言·笔记·后端·学习·golang
寻星探路36 分钟前
JAVA与C语言之间的差异(二)
java·开发语言
灰阳阳1 小时前
RabbitMQ的高级特性
java·rabbitmq·java-rabbitmq