在多线程编程中,线程之间的同步是一个常见且重要的任务。有时候,我们可能需要让一个线程等待另一个线程完成某个操作,但又不想持有被等待线程的引用。这可以通过多种方式实现,以下是一些实用的技巧解析。
1. 使用 CountDownLatch
CountDownLatch 是 Java 中一个非常有用的工具类,它允许一个或多个线程等待一组事件发生。CountDownLatch 的核心思想是维护一个计数器,这个计数器初始值由构造函数指定,每次调用 countDown() 方法时,计数器减一。当计数器减到零时,所有等待的线程将被唤醒。
import java.util.concurrent.CountDownLatch;
public class CountDownLatchExample {
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(1);
new Thread(() -> {
System.out.println("子线程开始执行");
// 假设这里有一些耗时操作
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("子线程执行完毕");
latch.countDown();
}).start();
System.out.println("主线程等待子线程完成");
latch.await();
System.out.println("主线程继续执行");
}
}
2. 使用 CyclicBarrier
CyclicBarrier 也是一个线程同步工具,它允许一组线程到达一个屏障点(barrier),然后所有线程等待,直到所有线程都到达屏障点后,再继续执行。CyclicBarrier 可以重用,这意味着它可以被多次设置。
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierExample {
public static void main(String[] args) {
CyclicBarrier barrier = new CyclicBarrier(2, () -> {
System.out.println("所有线程都已到达屏障点");
});
new Thread(() -> {
System.out.println("线程1开始执行");
try {
barrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println("线程1执行完毕");
}).start();
new Thread(() -> {
System.out.println("线程2开始执行");
try {
barrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println("线程2执行完毕");
}).start();
}
}
3. 使用 Semaphore
Semaphore 是一个信号量,它可以控制对共享资源的访问。当多个线程需要访问同一资源时,可以通过 Semaphore 来控制访问的线程数量。Semaphore 也可以用来实现线程间的等待和通知。
import java.util.concurrent.Semaphore;
public class SemaphoreExample {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(1);
new Thread(() -> {
System.out.println("线程1开始执行");
try {
semaphore.acquire();
// 假设这里有一些耗时操作
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
}
System.out.println("线程1执行完毕");
}).start();
new Thread(() -> {
System.out.println("线程2开始执行");
try {
semaphore.acquire();
// 假设这里有一些耗时操作
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
}
System.out.println("线程2执行完毕");
}).start();
}
}
4. 使用 Future 和 FutureTask
Future 和 FutureTask 是 Java 中用于异步编程的工具。Future 表示异步计算的结果,而 FutureTask 是 Future 的实现类。通过 Future,你可以提交一个任务给线程池,然后等待任务完成。
import java.util.concurrent.*;
public class FutureExample {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> future = executor.submit(() -> {
// 假设这里有一些耗时操作
Thread.sleep(1000);
return "任务完成";
});
System.out.println("主线程等待任务完成");
String result = future.get();
System.out.println("任务结果:" + result);
executor.shutdown();
}
}
通过以上几种方法,你可以在不持有对象引用的情况下,实现线程间的等待和通知。这些方法各有特点,可以根据实际需求选择合适的方式。
