JAVA和Nginx 教程大全

网站首页 > 精选教程 正文

如何在 Java 中控制多个线程的执行顺序?详细介绍一下?

wys521 2024-11-19 12:52:45 精选教程 19 ℃ 0 评论

在Java中控制多线程执行顺序的方式有很多种,下面我们就来介绍一下再开发中常用的控制多线程执行顺序的方式。

join() 方法

Thread.join()方法可以让一个线程等待另一个线程执行完成后再继续执行。例如,线程A可以调用线程B的join()方法,线程A会等待线程B执行完成后才会继续执行,如下所示。

public class JoinExample {
    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            System.out.println("Thread 1 is running");
        });
        
        Thread thread2 = new Thread(() -> {
            System.out.println("Thread 2 is running");
        });
        
        thread1.start();
        try {
            thread1.join(); // 主线程等待 thread1 结束
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        thread2.start();
    }
}

在上述代码中,main方法会等待thread1完成后再执行thread2,保证了执行顺序。

wait() 和 notify() 机制

当然除了上面的方式之外,Java中还提供了两个对象级的方法,可以通过锁机制实现线程间通信和顺序控制。通常结合synchronized关键字来实现同步,如下所示。

public class WaitNotifyExample {
    private static final Object lock = new Object();
    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            synchronized (lock) {
                System.out.println("Thread 1 is running");
                lock.notify(); // 唤醒等待的线程
            }
        });
        
        Thread thread2 = new Thread(() -> {
            synchronized (lock) {
                try {
                    lock.wait(); // 等待唤醒
                    System.out.println("Thread 2 is running");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        
        thread2.start();
        thread1.start();
    }
}

在上面的代码中,thread2会等待,直到thread1执行完并调用lock.notify()后才继续执行。这可以确保thread1先于thread2执行。

CountDownLatch

CountDownLatch是一个同步工具类,可以允许一个或多个线程等待其他线程完成操作。其内部设置了一个计数器,当计数器倒数到零的时候释放所有等待线程,如下所示。

import java.util.concurrent.CountDownLatch;

public class CountDownLatchExample {
    public static void main(String[] args) {
        CountDownLatch latch = new CountDownLatch(1);
        
        Thread thread1 = new Thread(() -> {
            System.out.println("Thread 1 is running");
            latch.countDown(); // 计数器减一
        });
        
        Thread thread2 = new Thread(() -> {
            try {
                latch.await(); // 等待计数器归零
                System.out.println("Thread 2 is running");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        
        thread2.start();
        thread1.start();
    }
}

这里的thread2会在latch计数器减到0之前保持等待,而thread1在完成任务后调用countDown(),使thread2可以继续执行。

使用CyclicBarrier

CyclicBarrier允许一组线程互相等待,直到它们都达到一个共同的屏障点。适合用于需要多个线程同步执行到某个步骤的场景。

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierExample {
    public static void main(String[] args) {
        CyclicBarrier barrier = new CyclicBarrier(2); // 需要 2 个线程到达屏障点

        Thread thread1 = new Thread(() -> {
            try {
                System.out.println("Thread 1 is running");
                barrier.await(); // 等待其他线程
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
            System.out.println("Thread 1 continues");
        });

        Thread thread2 = new Thread(() -> {
            try {
                System.out.println("Thread 2 is running");
                barrier.await(); // 等待其他线程
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
            System.out.println("Thread 2 continues");
        });

        thread1.start();
        thread2.start();
    }
}

在这个例子中,thread1和thread2都调用barrier.await(),只有两个线程都到达屏障点后才会继续执行“Thread X continues”。

使用Semaphore

Semaphore是一种计数信号量,用来控制访问特定资源的线程数量。在需要顺序时,可以设置信号量的初始许可数为0,让线程互相通知。

import java.util.concurrent.Semaphore;

public class SemaphoreExample {
    private static Semaphore semaphore = new Semaphore(0);

    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            System.out.println("Thread 1 is running");
            semaphore.release(); // 增加一个许可
        });

        Thread thread2 = new Thread(() -> {
            try {
                semaphore.acquire(); // 等待一个许可
                System.out.println("Thread 2 is running");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        thread2.start();
        thread1.start();
    }
}

在这里,thread2会在semaphore获得许可之前阻塞,而thread1在完成后通过调用release() 来释放一个许可,使得thread2得以继续执行。

使用Future 和 ExecutorService

ExecutorService提供了异步任务执行的控制,可以提交多个任务,并通过Future.get()阻塞地等待任务完成,从而实现顺序控制。

import java.util.concurrent.*;

public class FutureExample {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(2);

        Callable<String> task1 = () -> {
            System.out.println("Task 1 is running");
            return "Result of Task 1";
        };

        Callable<String> task2 = () -> {
            System.out.println("Task 2 is running");
            return "Result of Task 2";
        };

        try {
            Future<String> future1 = executor.submit(task1);
            future1.get(); // 等待 task1 完成
            
            Future<String> future2 = executor.submit(task2);
            future2.get(); // 等待 task2 完成
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        } finally {
            executor.shutdown();
        }
    }
}

这里,future1.get()会等待task1执行完成,然后才会执行task2。

总结

上面这些方式提供了在Java中控制线程执行顺序的常用方式,这些方法在不同场景下可以灵活使用,我们可以根据实际情况选择合适的机制来控制线程的执行顺序。

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表