原创

多线程间通信常用方法及简单示例(一)

温馨提示:
本文最后更新于 2024年05月15日,已超过 252 天没有更新。若文章内的图片失效(无法正常加载),请留言反馈或直接联系我

在Java中,多线程之间通信的方式有很多,下面我们介绍几个常用通信方法并给出简单示例:

共享变量

static int sharedVariable = 0;
static void sharedVariableExample() throws InterruptedException{
    Thread thread = new Thread(() -> {
        for (int i = 0; i < 10; i++) {
            sharedVariable++;
        }
    });
    thread.start();
    thread.join();
    logger.info("主线程获取共享变量值:{}", sharedVariable);
}

上述示例中,主线程与子线程thread共享同一个变量sharedVariable,并在主线程中调用join()方法,该方法会等待子线程执行完成后继续执行,在thread线程修改了变量的值之后,主线程就可以读取到修改后的值

wait()和notify()/notifyAll()

static final Object LOCK = new Object();
static boolean flag = true;
static void notifyExample() {
    new Thread(() -> {
        synchronized (LOCK) {
            while (flag) {
                try {
                    LOCK.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            logger.info("线程1被唤醒并获取读取到flag属性值");
        }
    }).start();

    new Thread(() -> {
        synchronized (LOCK) {
            flag = false;
            LOCK.notify();
            logger.info("线程2设置flag为false并唤醒线程1");
        }
    }).start();
}

通过Object类的wait()、notify()和notifyAll()方法实现线程间的等待和通知机制。在上述示例中线程1调用wait()方法等待线程2的唤醒,线程2通过notify()或notifyAll()方法来将其唤醒线程1继续运行

CountDownLatch

static void countDownLatchExample() throws InterruptedException {
    CountDownLatch latch = new CountDownLatch(1);
    new Thread(() -> {
        try {
            logger.info("线程1正在等待主线程释放");
            latch.await();
            logger.info("线程1等到主线程释放");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }).start();
    logger.info("模拟其他线程执行任务");
    Thread.sleep(1000);
    logger.info("任务执行完成计数器减一");
    latch.countDown();
}

CountDownLatch是一种多线程同步工具,它允许一个或多个线程等待其他线程完成操作后再继续执行。它通过一个计数器设置初始线程的数量,每个线程完成任务时都将计数器减一,当计数器减为0时,所有等待的线程被唤醒继续执行

Future和CompletableFuture

static void futureExample() {
    ExecutorService executor = Executors.newSingleThreadExecutor();
    // 使用 Future
    Future<Integer> future = executor.submit(() -> {
        // 模拟耗时操作
        Thread.sleep(500);
        return 42;
    });
    logger.info("Future已提交");
    try {
        logger.info("获取Future的结果: {}", future.get());
    } catch (InterruptedException | ExecutionException e) {
        e.printStackTrace();
    }
    executor.shutdown();
}
static void completableFutureExample() {
    CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return 42;
    });
    logger.info("CompletableFuture已提交");
    try {
        logger.info("获取CompletableFuture的结果: {}", completableFuture.get());
    } catch (InterruptedException | ExecutionException e) {
        e.printStackTrace();
    }
}

通过get()方法获取异步任务的执行结果

管道

static void pipedExample() throws IOException {
    PipedOutputStream outputStream = new PipedOutputStream();
    PipedInputStream inputStream = new PipedInputStream(outputStream);
    // 发送线程
    new Thread(() -> {
        try {
            String message = "Hello, World!";
            outputStream.write(message.getBytes());
            outputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }).start();

    // 接收线程
    new Thread(() -> {
        try {
            byte[] buffer = new byte[1024];
            int bytesRead = inputStream.read(buffer);
            logger.info("接收线程读取到管道数据:{}", new String(buffer, 0, bytesRead));
            inputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }).start();
}

管道(PipedInputStream和PipedOutputStream、PipedReader和PipedWriter)提供了一种在两个线程之间传输数据的机制。一个线程向管道中写入数据,另一个线程则从管道中读取数据,从而实现线程间通信

我们来测试以下以上方法的执行结果
线程间通信示例结果

每种通信方式都有其适用的场景,实际场景中需要根据具体的需求和情况选择合适的线程通信方式。下一篇我们给出更多线程间通信的方式

正文到此结束
本文目录