深入理解高并发编程 - 分析线程池中 Worker 线程的执行流程

“Worker” 类是线程池中的一个核心组件,用于执行提交给线程池的任务。下面是对 “Worker” 类的简要分析,涵盖了其主要属性和关键方法,基于 Java 8 版本的源码。

public class Worker extends AbstractQueuedSynchronizer implements Runnable {
    final ThreadPoolExecutor executor;
    Runnable task;
    volatile Thread thread;

    Worker(ThreadPoolExecutor executor) {
        this.executor = executor;
    }

    @Override
    public void run() {
        runWorker(this);
    }

    protected boolean isHeldExclusively() {
        return getState() != 0;
    }

    protected boolean tryAcquire(int unused) {
        if (compareAndSetState(0, 1)) {
            setExclusiveOwnerThread(Thread.currentThread());
            return true;
        }
        return false;
    }

    protected boolean tryRelease(int unused) {
        setExclusiveOwnerThread(null);
        setState(0);
        return true;
    }

    public void lock() {
        acquire(1);
    }

    public boolean tryLock() {
        return tryAcquire(1);
    }

    public void unlock() {
        release(1);
    }

    public boolean isLocked() {
        return isHeldExclusively();
    }

    void interruptIfStarted() {
        Thread t;
        if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
            try {
                t.interrupt();
            } catch (SecurityException ignore) {
            }
        }
    }

    void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.task;
        w.task = null;
        w.unlock();
        boolean completedAbruptly = true;
        try {
            while (task != null || (task = getTask()) != null) {
                w.lock();
                if ((runStateAtLeast(executor.ctl.get(), STOP) || (Thread.interrupted() && runStateAtLeast(executor.ctl.get(), STOP))) && !wt.isInterrupted()) {
                    wt.interrupt();
                }
                try {
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
                        task.run();
                    } catch (RuntimeException x) {
                        thrown = x; throw x;
                    } catch (Error x) {
                        thrown = x; throw x;
                    } catch (Throwable x) {
                        thrown = x; throw new Error(x);
                    } finally {
                        afterExecute(task, thrown);
                    }
                } finally {
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);
        }
    }
}

这是 “Worker” 类的核心部分的代码分析:

ThreadPoolExecutor executor: 引用持有当前 "Worker" 对象的线程池。

Runnable task: 当前 "Worker" 线程需要执行的任务。

volatile Thread thread: 当前 "Worker" 线程。

run(): "Worker" 线程的入口点,调用 runWorker(this) 开始执行任务。

tryAcquire(int unused): 尝试获取 Worker 独占锁,用于控制 Worker 线程的执行状态。

tryRelease(int unused): 释放 Worker 独占锁。

runWorker(Worker w): 实际的任务执行方法,这是 Worker 线程执行任务的核心逻辑。在循环中,它会尝试获取 Worker 独占锁,然后从任务队列中获取任务并执行。

interruptIfStarted(): 如果线程还在运行,尝试中断线程。

beforeExecute(Thread t, Runnable r): 在任务执行前的钩子方法。

afterExecute(Runnable r, Throwable t): 在任务执行后的钩子方法。

在这个简要的分析中,“Worker” 类负责维护线程池中的一个工作线程,它会循环地从任务队列中获取任务并执行。

1、ThreadPoolExecutor executor: 引用持有当前 “Worker” 对象的线程池。

在 Java 中,“Worker” 类是用来执行线程池中的任务的工作线程。每个 “Worker” 对象都属于特定的线程池,而 ThreadPoolExecutor executor 字段就是用来持有当前 “Worker” 对象所属的线程池的引用。

这个引用的作用是使每个 “Worker” 对象能够与其所属的线程池进行交互,包括获取线程池的状态、执行任务、管理线程池等。通过这个引用,“Worker” 对象可以通过调用线程池的方法来执行任务、获取任务队列、修改线程池的配置等。

在 “Worker” 类的构造函数中,可以看到 ThreadPoolExecutor executor 字段的初始化:

Worker(ThreadPoolExecutor executor) {
    this.executor = executor;
}

通过这种方式,在创建 “Worker” 对象时,传入了其所属的线程池实例,从而建立了 “Worker” 对象与线程池之间的关联关系。

这种关联关系允许 “Worker” 对象在执行任务时能够与线程池进行协作,确保任务的正确执行、适时地返回结果、适当地管理线程池的状态等。这样,“Worker” 类就成为了线程池内部的重要组成部分,通过它,线程池能够更好地管理任务的执行和线程资源的利用。

2、Runnable task: 当前 “Worker” 线程需要执行的任务

在线程池中,“Worker” 类用于执行提交给线程池的任务。每个 “Worker” 对象代表一个工作线程,它从任务队列中获取任务并执行。Runnable task 字段是用来存储当前 “Worker” 线程需要执行的任务。

当 “Worker” 线程被唤醒并准备执行任务时,它会从任务队列中获取一个待执行的任务,并将该任务存储在 task 字段中。然后,“Worker” 线程会执行这个任务的 run() 方法,完成实际的工作。任务可以是实现了 Runnable 接口的任何类的实例,这使得线程池可以执行各种不同类型的任务。

下面是一个简化的示例,展示了 “Worker” 类中 Runnable task 字段的使用:

public class Worker implements Runnable {
    private Runnable task; // 当前需要执行的任务

    public Worker(Runnable task) {
        this.task = task;
    }

    @Override
    public void run() {
        if (task != null) {
            task.run(); // 执行任务的 run() 方法
        }
    }
}

// 在线程池中使用 Worker 类执行任务
public class ThreadPool {
    // ...

    public void execute(Runnable task) {
        Worker worker = new Worker(task);
        // 启动线程执行任务
        threadPoolExecutor.execute(worker);
    }

    // ...
}

在这个示例中,“Worker” 类的 task 字段存储了待执行的任务,而 run() 方法会执行该任务的 run() 方法。通过将任务传递给 “Worker” 对象的构造函数,线程池可以将任务委派给 “Worker” 线程来执行。这样,线程池就能够以多线程的方式并发地执行多个任务。

3、volatile Thread thread: 当前 “Worker” 线程。

volatile Thread thread 是 “Worker” 类中的一个字段,用于存储当前 “Worker” 线程的引用。在多线程环境中,使用 volatile 修饰符可以确保线程间的可见性和内存屏障,从而更好地管理线程状态。

在 “Worker” 类中,volatile Thread thread 字段的作用是跟踪当前的工作线程。这个字段可以帮助线程池及时检测和管理工作线程的状态,例如在需要中断工作线程时,或者在线程池的关闭过程中。

以下是一个简化示例,展示了 volatile Thread thread 字段的使用:

public class Worker implements Runnable {
    private volatile Thread thread; // 当前 Worker 线程

    @Override
    public void run() {
        thread = Thread.currentThread();
        // 执行任务逻辑
    }

    public void interrupt() {
        if (thread != null) {
            thread.interrupt(); // 中断当前 Worker 线程
        }
    }
}

在这个示例中,当 “Worker” 线程开始执行任务时,会将当前线程的引用存储在 thread 字段中。这样,其他地方就可以通过 thread 字段来引用和操作当前的 “Worker” 线程。在 interrupt() 方法中,可以中断当前 “Worker” 线程,从而有效地终止它的执行。

总之,volatile Thread thread 字段是用来存储当前 “Worker” 线程的引用,通过它可以进行线程状态的管理和操作,以实现对工作线程的控制。

4、run(): “Worker” 线程的入口点,调用 runWorker(this) 开始执行任务。

在 “Worker” 类中,run() 方法是 “Worker” 线程的入口点,它会调用 runWorker(this) 方法来开始执行任务。这个过程是线程池中的核心逻辑之一,用于实际执行提交给线程池的任务。

以下是 “Worker” 类的相关代码片段,展示了 run() 方法的调用:

public class Worker implements Runnable {
    // ...

    @Override
    public void run() {
        runWorker(this); // 调用核心方法开始执行任务
    }

    // ...
}

在这里,run() 方法被调用时,会将当前 “Worker” 对象作为参数传递给 runWorker() 方法。runWorker() 方法是实际执行任务的核心逻辑,会从任务队列中获取任务并执行,以完成线程池的任务调度功能。

请注意,这只是一个简化的示例,实际源代码中可能会包含更多细节和额外的处理。通过调用 runWorker() 方法,“Worker” 线程会开始从任务队列中获取任务并逐个执行,直到没有更多的任务可执行为止。这种机制实现了线程池中任务的异步执行。

5、tryAcquire(int unused): 尝试获取 Worker 独占锁,用于控制 Worker 线程的执行状态。

在 “Worker” 类中,tryAcquire(int unused) 方法是用于尝试获取 Worker 独占锁的方法,用于控制 “Worker” 线程的执行状态。这个方法是 AbstractQueuedSynchronizer 类中的一个方法,它实现了独占式的同步机制,以确保只有一个线程能够持有该锁。

以下是 “Worker” 类中 tryAcquire(int unused) 方法的解释:

protected boolean tryAcquire(int unused) {
    if (compareAndSetState(0, 1)) { // 使用 CAS 设置状态为 1,表示锁已被获取
        setExclusiveOwnerThread(Thread.currentThread()); // 设置独占锁的拥有者为当前线程
        return true;
    }
    return false;
}

解释如下:

compareAndSetState(0, 1): 使用 CAS(比较并交换)操作尝试将状态从 0 设置为 1,表示尝试获取锁。如果当前状态为 0,并且成功将状态设置为 1,说明锁已被当前线程获取。

setExclusiveOwnerThread(Thread.currentThread()): 如果成功获取了锁,将独占锁的拥有者设置为当前线程,这样其他线程就无法再获取该锁。

返回值:如果成功获取锁,返回 true,表示当前线程已获得锁。如果锁已被其他线程持有,返回 false。

这个方法的作用是确保同一时间只有一个 “Worker” 线程能够获取独占锁,从而控制线程的执行状态。在 “Worker” 类中,独占锁的获取与释放会在执行任务的过程中进行,以保证线程安全和任务的顺序性。

6、tryRelease(int unused): 释放 Worker 独占锁。

在 “Worker” 类中,tryRelease(int unused) 方法是用于释放 Worker 独占锁的方法。这个方法是 AbstractQueuedSynchronizer 类中的一个方法,用于实现独占式同步的解锁操作。

以下是 “Worker” 类中 tryRelease(int unused) 方法的解释:

protected boolean tryRelease(int unused) {
    setExclusiveOwnerThread(null); // 将独占锁的拥有者设置为 null,表示释放锁
    setState(0); // 将状态设置为 0,表示锁已释放
    return true;
}

解释如下:

setExclusiveOwnerThread(null): 将独占锁的拥有者设置为 null,表示释放锁。这样其他线程就有机会获取这个锁。

setState(0): 将状态设置为 0,表示锁已释放。

返回值:无论是否成功释放锁,都返回 true。

这个方法的作用是释放 “Worker” 独占锁,允许其他线程尝试获取锁。在 “Worker” 类中,独占锁的释放通常会在任务执行完成后进行,以便其他线程有机会获取锁并执行任务。

需要注意的是,tryRelease(int unused) 方法是为独占式同步设计的,用于实现独占锁的获取与释放。它是线程安全编程中常见的同步机制之一,确保了多线程环境下的互斥访问。

7、runWorker(Worker w): 实际的任务执行方法,这是 Worker 线程执行任务的核心逻辑。在循环中,它会尝试获取 Worker 独占锁,然后从任务队列中获取任务并执行。

runWorker(Worker w) 方法是 “Worker” 线程实际执行任务的核心逻辑,包含了从任务队列中获取任务并执行的过程。下面是对 runWorker(Worker w) 方法的更详细解释:

void runWorker(Worker w) {
    Thread wt = Thread.currentThread(); // 获取当前线程
    Runnable task = w.task; // 获取当前 Worker 的任务
    w.task = null; // 清空任务,准备获取下一个任务
    w.unlock(); // 释放 Worker 的独占锁
    boolean completedAbruptly = true;

    try {
        while (task != null || (task = getTask()) != null) { // 循环执行任务
            w.lock(); // 尝试获取 Worker 的独占锁
            // ...
            try {
                beforeExecute(wt, task); // 在任务执行前的钩子方法
                // 执行任务逻辑
                task.run();
                // ...
            } finally {
                task = null;
                w.completedTasks++;
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
        processWorkerExit(w, completedAbruptly);
    }
}

解释如下:

Thread wt = Thread.currentThread();: 获取当前正在执行的线程,即当前的 "Worker" 线程。

Runnable task = w.task;: 获取当前 "Worker" 线程的任务,这个任务是通过 execute() 方法提交的。

w.task = null;: 清空当前 "Worker" 线程的任务字段,为获取下一个任务做准备。

w.unlock();: 释放当前 "Worker" 线程的独占锁,使得其他线程有机会获取锁并执行任务。

while (task != null || (task = getTask()) != null): 进入循环,循环条件是当前任务不为空或者从任务队列中获取一个任务不为空。

w.lock();: 尝试获取当前 "Worker" 线程的独占锁,以便执行任务。

在循环内部,先调用 beforeExecute(wt, task) 方法,这是一个钩子方法,用于在任务执行前执行一些操作。

执行任务的逻辑,即调用 task.run() 方法来实际执行任务。

task = null; w.completedTasks++; w.unlock();: 在任务执行完成后,清空任务字段,增加已完成任务数,释放独占锁。

循环继续,获取下一个任务并继续执行。

completedAbruptly = false;: 在循环正常结束时,设置 completedAbruptly 为 false,表示没有异常终止。

在 finally 块中,调用 processWorkerExit(w, completedAbruptly),这是用来处理 "Worker" 线程退出的逻辑。

总之,runWorker(Worker w) 方法负责循环地从任务队列中获取任务并执行,是 “Worker” 线程执行任务的核心逻辑。这个方法在线程池中起到了关键的作用,确保任务按照顺序被执行。

8、interruptIfStarted(): 如果线程还在运行,尝试中断线程。

interruptIfStarted() 方法在 “Worker” 类中是用来尝试中断线程的方法。它会检查当前 “Worker” 线程的状态,如果线程仍在运行并且没有被中断,就会尝试对线程进行中断操作。

以下是对 interruptIfStarted() 方法的更详细解释:

void interruptIfStarted() {
    Thread t;
    if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
        try {
            t.interrupt(); // 尝试中断线程
        } catch (SecurityException ignore) {
        }
    }
}

解释如下:

getState() >= 0: 检查当前 "Worker" 线程的状态是否大于等于 0,这是为了确保线程未被中止。

(t = thread) != null: 检查 thread 字段是否为非空,即检查当前 "Worker" 线程是否已启动。

!t.isInterrupted(): 检查当前线程是否已被中断。

如果满足上述条件,说明当前线程仍在运行且未被中断,就会尝试对线程进行中断操作。

t.interrupt(): 尝试中断线程,即设置线程的中断状态为 true。

catch (SecurityException ignore): 可能会捕获到安全异常,这里简单地忽略该异常。

总之,interruptIfStarted() 方法用于在适当的时候尝试中断 “Worker” 线程,以便在需要终止线程执行时能够及时中断线程。这在线程池的关闭和任务取消等场景中是很有用的,能够帮助管理线程的执行状态。

9、beforeExecute(Thread t, Runnable r): 在任务执行前的钩子方法。

在 “Worker” 类中,beforeExecute(Thread t, Runnable r) 方法是一个钩子方法,用于在任务执行前执行一些操作。这个方法可以被子类重写,以实现一些定制的逻辑,例如任务执行前的准备工作、日志记录、性能监控等。

以下是 beforeExecute(Thread t, Runnable r) 方法的一种示例实现:

void beforeExecute(Thread t, Runnable r) {
    System.out.println("Thread " + t.getName() + " is about to execute task: " + r.toString());
}

在这个示例中,beforeExecute(Thread t, Runnable r) 方法被重写,它会在任务执行前打印一条日志,显示即将执行的任务的信息和线程的名称。

在实际使用中,可以根据需求在 beforeExecute(Thread t, Runnable r) 方法中添加自定义的逻辑,以便在任务执行前进行一些特定的操作。这可以更好地监控和管理线程池中任务的执行过程。

10、afterExecute(Runnable r, Throwable t): 在任务执行后的钩子方法。

在 “Worker” 类中,afterExecute(Runnable r, Throwable t) 方法是一个钩子方法,用于在任务执行后执行一些操作。这个方法可以被子类重写,以实现一些定制的逻辑,例如任务执行后的清理工作、结果处理、异常处理等。

以下是 afterExecute(Runnable r, Throwable t) 方法的一种示例实现:

void afterExecute(Runnable r, Throwable t) {
    if (t != null) {
        System.err.println("Task execution resulted in an error: " + t.getMessage());
    } else {
        System.out.println("Task execution completed successfully: " + r.toString());
    }
}

在这个示例中,afterExecute(Runnable r, Throwable t) 方法被重写,它会根据任务执行的结果打印相应的日志,如果任务执行过程中出现了异常,会输出错误信息,否则会显示任务执行成功。

在实际使用中,可以根据需求在 afterExecute(Runnable r, Throwable t) 方法中添加自定义的逻辑,以便在任务执行后进行一些特定的操作。这可以更好地监控任务的执行情况、处理任务执行过程中的异常,以及在任务完成后进行一些必要的清理工作。

总结:执行流程

整个线程池中 “Worker” 线程的执行流程如下:

当有任务需要执行时,线程池会创建一个新的 "Worker" 对象,其中包含一个引用指向线程池本身,并将待执行的任务传递给 "Worker" 对象。

"Worker" 对象中的 run() 方法被调用,作为线程的入口点。在 run() 方法中,会调用 runWorker(this) 方法开始执行任务。

在 runWorker(Worker w) 方法中,首先获取当前线程的引用,并获取当前线程需要执行的任务。

任务执行前,会调用 beforeExecute(Thread t, Runnable r) 方法,这是一个钩子方法,可以在任务执行前执行一些自定义的操作。

"Worker" 线程尝试获取独占锁(tryAcquire(int unused) 方法),确保同一时间只有一个线程能够执行任务。如果成功获取锁,开始执行任务。

执行任务的逻辑在任务的 run() 方法中。这是实际完成任务工作的地方。

任务执行完成后,会调用 afterExecute(Runnable r, Throwable t) 方法,另一个钩子方法,用于在任务执行后执行一些自定义的操作,例如结果处理或异常处理。

"Worker" 线程会循环尝试获取任务并执行,直到任务队列中没有更多的任务为止。

循环中,如果有需要中断线程的信号,可以调用 interruptIfStarted() 方法尝试中断线程。

当任务队列中没有任务时,"Worker" 线程会结束循环。

在循环结束后,会调用 processWorkerExit(w, completedAbruptly) 方法,用于处理 "Worker" 线程退出的逻辑,包括清理和资源回收。

整个流程涵盖了任务的提交、“Worker” 线程的创建和执行、任务的处理、线程的中断以及线程的退出处理等。通过这个流程,线程池能够有效地管理并发执行的任务,提高系统的性能和资源利用率。

你可能感兴趣的:(#,高并发编程,java)