一、使用join()方法进行线程等待
join()方法是Thread类提供的一个实例方法,它允许一个线程等待另一个线程的完成。当调用某个线程的join()方法时,当前线程会暂停执行,直到被调用线程执行完毕,当前线程才会继续执行。如果被调用线程在join()方法调用前已经执行完毕,那么该方法会立即返回。
下面是一个使用join()方法的示例:
Thread thread = new Thread(new Runnable() { @Override public void run() { System.out.println("子线程开始执行"); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("子线程执行完毕"); } }); thread.start(); System.out.println("主线程开始执行"); try { thread.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("主线程执行完毕");
在上述示例中,我们创建了一个新的线程,该线程会等待5秒后执行完毕。在主线程中,我们调用了thread.join()方法,因此主线程会等待子线程执行完毕后再继续执行。输出结果如下:
主线程开始执行 子线程开始执行 子线程执行完毕 主线程执行完毕
二、使用wait()和notify()方法进行线程等待
wait()和notify()方法是Object类提供的实例方法,它们可以实现线程间的等待和唤醒。当一个线程执行wait()方法时,它会释放它所持有的锁,并进入等待状态,直到其它线程调用notify()方法或notifyAll()方法来唤醒它。当一个线程执行notify()方法时,它会唤醒一个正处于等待状态中的线程(注意是随机唤醒一个线程),使其继续执行。
下面是一个使用wait()和notify()方法的示例,该示例演示了如何使用两个线程交替输出1到10:
class PrintThread implements Runnable { private int num; private Object lock; public PrintThread(int num, Object lock) { this.num = num; this.lock = lock; } @Override public void run() { synchronized (lock) { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + " " + num * 10 + i); lock.notify(); if (i != 9) { try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } } public static void main(String[] args) { Object lock = new Object(); Thread thread1 = new Thread(new PrintThread(1, lock)); Thread thread2 = new Thread(new PrintThread(2, lock)); thread1.start(); thread2.start(); }
在上述示例中,我们创建了两个线程,分别输出1到10和11到20。在PrintThread类的run()方法中,我们首先获取lock对象的锁,并使用wait()方法使线程进入等待状态,直到另一个线程调用notify()方法来唤醒它。当一个线程输出完一行后,使用notify()方法唤醒等待状态的线程,然后自己进入等待状态,等待其它线程唤醒。这样,我们就实现了两个线程交替输出1到20。
三、使用CountDownLatch类进行线程等待
CountDownLatch类是在Java 5中引入的,并且它是java.util.concurrent包中的一个类。它是一个同步的辅助类,用来协调多个线程之间的同步。CountDownLatch的功能是阻塞一个线程或多个线程,直到它所计数的线程执行完毕才能向下执行。它使用一个计数器来实现这个功能,计数器初始值为线程数,当某个线程执行完毕时,计数器减1,当计数器减为0时,所有等待该计数器的线程将会被唤醒。
下面是一个使用CountDownLatch类的示例:
public static void main(String[] args) throws InterruptedException { int threadCount = 10; CountDownLatch latch = new CountDownLatch(threadCount); for (int i = 0; i < threadCount; i++) { new Thread(() -> { System.out.println(Thread.currentThread().getName() + " 执行完毕"); latch.countDown(); }).start(); } latch.await(); System.out.println("所有线程执行完毕"); }
在上述示例中,我们使用了一个CountDownLatch对象来等待10个线程执行完毕。当一个线程执行完毕时,我们调用latch.countDown()方法来减少计数器的值,当计数器减为0时,主线程调用latch.await()方法进入等待状态,直到所有线程执行完毕,主线程才会继续执行。输出结果如下:
Thread-0 执行完毕 Thread-1 执行完毕 Thread-3 执行完毕 Thread-2 执行完毕 Thread-4 执行完毕 Thread-8 执行完毕 Thread-9 执行完毕 Thread-5 执行完毕 Thread-7 执行完毕 Thread-6 执行完毕 所有线程执行完毕
四、使用CyclicBarrier类进行线程等待
CyclicBarrier类也是在Java 5中引入的,它也是java.util.concurrent包中的一个类。CyclicBarrier可以被用来协调多个线程之间的同步。它可以让一组线程在到达某个屏障时被阻塞,直到最后一个线程到达屏障时,所有被阻塞的线程才能继续执行。CyclicBarrier和CountDownLatch的区别在于,CyclicBarrier可以用于一组线程间相互等待,而CountDownLatch只能用于一个线程等待其它线程执行完毕。
下面是一个使用CyclicBarrier类的示例:
class WorkerThread implements Runnable { private String name; private CyclicBarrier barrier; public WorkerThread(String name, CyclicBarrier barrier) { this.name = name; this.barrier = barrier; } @Override public void run() { try { System.out.println(name + " 开始工作"); Thread.sleep((long) (Math.random() * 5000)); System.out.println(name + " 工作完毕,等待其它线程"); barrier.await(); System.out.println(name + " 继续执行"); } catch (InterruptedException | BrokenBarrierException e) { e.printStackTrace(); } } } public static void main(String[] args) throws InterruptedException { int threadCount = 5; CyclicBarrier barrier = new CyclicBarrier(threadCount, () -> { System.out.println("所有线程工作完毕,开始汇总结果"); }); for (int i = 0; i < threadCount; i++) { new Thread(new WorkerThread("线程" + i, barrier)).start(); } Thread.sleep(6000); }
在上述示例中,我们创建了5个线程,它们会随机工作一段时间后等待其它线程,最后再汇总工作结果。在WorkerThread类的run()方法中,我们使用CyclicBarrier类的await()方法来等待其它线程,当所有线程都到达屏障时,所有线程才能继续执行。输出结果如下:
线程0 开始工作 线程2 开始工作 线程4 开始工作 线程3 开始工作 线程1 开始工作 线程2 工作完毕,等待其它线程 线程0 工作完毕,等待其它线程 线程1 工作完毕,等待其它线程 线程3 工作完毕,等待其它线程 线程4 工作完毕,等待其它线程 所有线程工作完毕,开始汇总结果 线程4 继续执行 线程1 继续执行 线程0 继续执行 线程2 继续执行 线程3 继续执行
总结
Java中提供了多种方法来实现线程等待,包括使用join()方法、wait()和notify()方法、CountDownLatch类、CyclicBarrier类等。在实际代码编写中,我们需要根据具体的需求选择合适的方式来实现线程等待。无论是哪种方式,它们都可以帮助我们实现线程之间的同步,保证多个线程之间的协作。