Java线程同步和协作的5种方式

这篇具有很好参考价值的文章主要介绍了Java线程同步和协作的5种方式。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

问题

有3个线程为A,B,C,同时启动,C必须等待A和B完成才能继续执行,如何实现?
要求:仅使用 java 语言和它原生API

解法

  1. 使用 Java 中提供的 CountDownLatch 类实现线程之间的同步和协作

  2. 使用 Java 中提供的 CyclicBarrier 类实现线程之间的同步和协作

  3. 使用join()方法:Thread类提供了join()方法,可以让一个线程等待另一个线程执行完毕后再继续执行。在本例中,C线程可以在启动A和B线程后,分别调用它们的join()方法等待它们完成执行,然后再执行自己的任务。

  4. 使用wait()和notifyAll()方法:可以通过使用对象的wait()和notifyAll()方法来实现线程间的协作。在本例中,可以创建一个共享对象,在A、B线程执行完毕后调用该对象的notifyAll()方法通知C线程可以继续执行,而C线程在启动前需要调用该对象的wait()方法等待A、B线程执行完毕并通知自己。

  5. 使用Semaphore类:Semaphore类是一种计数信号量,可以限制同一时间内对共享资源的访问数量。在本例中,可以使用Semaphore类来创建一个初始值为0的信号量,A和B线程执行完毕后释放信号量,C线程需要先获取到两个信号量才能继续执行。

下面分别是演示:

CountDownLatch

CountDownLatch是Java中的一个工具类,可以用于控制线程执行顺序。它的作用就是允许一个或多个线程等待另外一组线程完成操作后再继续执行。

CountDownLatch包含一个计数器,这个计数器被初始化为一个正整数,表示需要等待完成的线程数量。当线程完成它的任务时,计数器的值减1。当计数器的值变为0时,表示所有需要等待的线程都已经完成了任务,此时等待中的线程会被唤醒继续执行。

下面是CountDownLatch的一个简单示例:

import java.util.concurrent.CountDownLatch;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(3); // 初始化计数器为 3

        Thread t1 = new Thread(() -> {
            System.out.println("Task 1 is running");
            try {
                Thread.sleep(1000); // 模拟耗时操作
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Task 1 is done");
            latch.countDown(); // 计数器减一
        });

        Thread t2 = new Thread(() -> {
            System.out.println("Task 2 is running");
            try {
                Thread.sleep(3000); // 模拟耗时操作
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Task 2 is done");
            latch.countDown(); // 计数器减一
        });

        Thread t3 = new Thread(() -> {
            System.out.println("Task 3 is running");
            try {
                Thread.sleep(2000); // 模拟耗时操作
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Task 3 is done");
            latch.countDown(); // 计数器减一
        });

        t1.start();
        t2.start();
        t3.start();

        latch.await(); // 等待计数器归零

        System.out.println("All tasks are done");
    }
}

在这个例子中,我们创建了一个CountDownLatch对象,并初始化计数器为3。然后启动了三个线程,每个线程执行一些耗时操作,并在最后调用countDown()方法将计数器减一。最后,调用await()方法等待计数器归零,然后输出"All tasks are done"。

需要注意的是,如果计数器的值已经变为0,那么对countDown()方法的调用不会产生任何效果。而对await()方法的调用会立即返回。因此,在使用CountDownLatch时,通常要保证计数器的值不会被错误地减多或减少。

使用 CyclicBarrier

CyclicBarrier 是另一种同步工具类,它可以使一组线程相互等待,直到所有线程都达到某个公共屏障点后再继续执行。在本例中,可以创建一个 CyclicBarrier 对象,设置等待的线程数为 2,当 A、B 线程完成任务后调用 await() 方法等待其他线程到达屏障点,而 C 线程在启动前需要调用 await() 方法等待 A、B 线程到达屏障点后再继续执行。

具体实现如下:

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

public class Main {
    public static void main(String[] args) throws InterruptedException {
        CyclicBarrier barrier = new CyclicBarrier(2, () -> {
            System.out.println("Thread C is running");

            try {
                Thread.sleep(1000); // 模拟C的耗时操作
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
             // 当A和B线程完成后,继续执行C线程的任务
            System.out.println("Thread C is done");
           
        });

        Thread A = new Thread(() -> {
            // 线程A的任务
            try {
                System.out.println("Thread A is running");
                Thread.sleep(1000); // // 模拟耗时操作
                System.out.println("Thread A is done");

                barrier.await();

            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
        });

        Thread B = new Thread(() -> {

            try {
                // 线程B的任务
                System.out.println("Thread B is running");
                Thread.sleep(1000); // // 模拟耗时操作
                System.out.println("Thread B is done");

                barrier.await();

            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }

        });

      

        A.start();
        B.start();

    }
}

使用 join 方法

是的,可以使用Java中的wait()和notifyAll()方法实现这种线程同步。

下面是一个示例代码,其中线程A和线程B工作完成后会调用notifyAll()方法,通知等待的线程C继续执行:
可以使用Java中的线程同步机制来实现这个需求,具体实现如下:

  1. 创建三个线程A、B、C,并且让它们同时执行:
Thread A = new Thread(() -> {
    // 线程A的任务
});

Thread B = new Thread(() -> {
    // 线程B的任务
});

Thread C = new Thread(() -> {
    // 线程C的任务
});
A.start();
B.start();
C.start();
  1. 在C线程中加入等待A和B线程的逻辑:
try {
    // 等待A线程完成
    A.join();
    
    // 等待B线程完成
    B.join();
} catch (InterruptedException e) {
    e.printStackTrace();
}

// 当A和B线程完成后,继续执行C线程的任务

完整代码如下所示:

public class Main {
    public static void main(String[] args) throws InterruptedException {

        Thread a = new Thread(() -> {
            System.out.println("Thread A is running");
            try {
                Thread.sleep(3000); // 模拟耗时操作
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Thread A is done");
        });

        Thread b = new Thread(() -> {
            System.out.println("Thread B is running");
            try {
                Thread.sleep(2000); // 模拟耗时操作
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Thread B is done");
        });

        Thread c = new Thread(() -> {
            try {
                // 等待其它线程执行完毕
                a.join();
                b.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Thread C is running");
        });

        a.start();
        b.start();
        c.start();

        c.join();

        System.out.println("All threads are done");
    }

}

使用 notify/wait 方法

是的,可以使用Java中的wait()和notifyAll()方法实现这种线程同步。

下面是一个示例代码,其中线程A和线程B工作完成后会调用notifyAll()方法,通知等待的线程C继续执行:

public class ThreadSync {
    public static void main(String[] args) throws InterruptedException {
        Object lock = new Object();
        boolean aDone = false;
        boolean bDone = false;

        // Thread A
        Thread threadA = new Thread(() -> {
            // Do some work
            System.out.println("Thread A is doing some work...");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            // Signal that work is done
            synchronized (lock) {
                aDone = true;
                lock.notifyAll();
            }
        });

        // Thread B
        Thread threadB = new Thread(() -> {
            // Do some work
            System.out.println("Thread B is doing some work...");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            // Signal that work is done
            synchronized (lock) {
                bDone = true;
                lock.notifyAll();
            }
        });

        // Thread C
        Thread threadC = new Thread(() -> {
            synchronized (lock) {
                while (!(aDone && bDone)) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                // Do some work
                System.out.println("Thread C is doing some work...");
            }
        });

        // Start all threads
        threadA.start();
        threadB.start();
        threadC.start();

        // Wait for all threads to finish
        threadA.join();
        threadB.join();
        threadC.join();

        System.out.println("All threads have finished.");
    }
}

在这个示例中,我们通过创建一个共享的锁对象,并使用它来同步不同线程之间的访问。当A和B线程完成工作时,它们会调用notifyAll()方法,通知等待的线程C继续执行。而线程C会在while循环内部等待,直到A和B都完成工作并调用notifyAll()方法后才继续执行。

需要注意的是,在使用wait()和notifyAll()方法时,必须在同步块内部调用它们。否则,将抛出IllegalMonitorStateException异常。

使用信号量 Semaphore

下面是使用Java语言和Semaphore实现的示例代码:

import java.util.concurrent.Semaphore;

public class Main {
    
    private static Semaphore semaphoreA = new Semaphore(1);
    private static Semaphore semaphoreB = new Semaphore(1);
    private static Semaphore semaphoreC = new Semaphore(0);

    public static void main(String[] args) throws InterruptedException {
        Thread threadA = new Thread(() -> {
            try {
                semaphoreA.acquire();
                System.out.println("Thread A is running");
                Thread.sleep(1000); // 模拟线程A执行时间
                System.out.println("Thread A is done");
                semaphoreC.release(); // 执行完释放semaphoreC
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                semaphoreA.release();
            }
        });

        Thread threadB = new Thread(() -> {
            try {
                semaphoreB.acquire();
                System.out.println("Thread B is running");
                Thread.sleep(2000); // 模拟线程B执行时间
                System.out.println("Thread B is done");
                semaphoreC.release(); // 执行完释放semaphoreC
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                semaphoreB.release();
            }
        });

        Thread threadC = new Thread(() -> {
            try {
                semaphoreC.acquire(2); // 等待2个信号量,即等待线程A和线程B执行完成
                System.out.println("Thread C is running");
                Thread.sleep(3000); // 模拟线程C执行时间
                System.out.println("Thread C is done");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        threadA.start();
        threadB.start();
        threadC.start();

        threadA.join();
        threadB.join();
        threadC.join();

        System.out.println("All threads are done");
    }
}

在上面的代码中,我们定义了三个Semaphore,分别为semaphoreA、semaphoreB和semaphoreC。semaphoreA和semaphoreB的初始信号量都设置为1,而semaphoreC的初始信号量为0。

线程A和线程B获取各自的Semaphore,执行完逻辑后释放semaphoreC信号量,线程C获取两个信号量后开始执行逻辑。

通过Semaphore的特性,实现了线程C必须等待线程A和线程B都执行完成才能开始执行的需求。

在这个问题描述中,有3个线程A、B、C,其中线程C必须等待线程A和线程B都执行完成才能开始执行。因此,我们需要使用Semaphore来控制线程的执行顺序。

在这个问题描述中,semaphoreA 和 semaphoreB 不是必须的。

定义了semaphoreA是为了保证在任何时候只有一个线程可以访问线程A的代码块。如果没有semaphoreA,可能会出现多个线程同时执行线程A的代码块,导致并发问题。

具体地说,在上面的Java代码示例中,我们定义了一个初始信号量为1的semaphoreA。当线程A需要执行其代码块时,它首先要获取semaphoreA的信号量,以保证只有一个线程可以进入代码块中执行。当线程A执行完了其代码块后,它会释放semaphoreA的信号量,以允许其他线程(如线程B)进入代码块中执行。

注意,semaphoreA只用于控制线程A的代码块的访问,而不影响线程B和线程C的执行。线程B也有自己的semaphoreB来控制其代码块的访问,而线程C则使用semaphoreC来等待线程A和线程B执行完成后再开始执行。

以上是几种常见的实现线程同步和协作的方式,您可以根据具体的需求选择最适合的方案。

本文完,希望能帮到你文章来源地址https://www.toymoban.com/news/detail-433089.html

到了这里,关于Java线程同步和协作的5种方式的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 以太坊节点启动及同步方式

    0.前置知识 节点:客户端的软件。全节点、轻节点 客户端:一种以太坊的实现,提供RPC API端点方便用户控制客户端,标准的JSON RPC每个客户端都可以使用。geth、OpenEthereum(停止维护) 轻量同步:下载所有区块头、区块数据并对其进行随机验证 通过客户端与以太坊网络进行交互

    2024年01月25日
    浏览(36)
  • 解决OBS同时录制电脑音频和人声时出现的声音不同步问题

    在obs同时录制电脑声音和人声时,比如在 唱歌 、 配音 时,录制的结果人声有明显的延迟,大约0.5-1秒左右的样子 麦克风 - 高级音频属性 调整麦克风的同步偏移,通常是人声有延迟,用负数表示提前多少毫秒,需要多次尝试确定真正的延迟时间 注: 这是目前唯一的一个人声

    2024年02月12日
    浏览(49)
  • 【QT专栏】QT中实现多线程的四种方式和线程同步

    目录 一、继承QThread 1.基本概念 2.操作流程 二、继承QObject(推荐) 1.基本概念 2.操作流程 三、继承QRunnable,配合QThreadPool实现多线程 1.外界通信 2.QMetaObject::invokeMethod()介绍 3.QMetaObject::invokeMethod()使用方式 四、使用QtConcurrent::run() 1.基本概念 2.操作流程 3.实现案例 五,线程同步

    2024年02月20日
    浏览(46)
  • 一文搞定Linux线程间通讯 / 线程同步方式-互斥锁、读写锁、自旋锁、信号量、条件变量、信号等等

    目录 线程间通讯 / 线程同步方式 锁机制 互斥锁(Mutex) 读写锁(rwlock) 自旋锁(spin) 信号量机制(Semaphore) 条件变量机制 信号(Signal) 线程间通讯 / 线程同步方式 p.s 以下有很多段落是直接引用,没有使用 markdown 的 “引用” 格式,出处均已放出。 参考 / 引用: 100as

    2024年02月10日
    浏览(44)
  • python - 线程的启动的几种方式

    本文主要给大家介绍python启动线程的四种方式 创建 Thread 对象,然后调用 start() 方法启动线程。 重写 run() 方法,并调用 start() 方法启动线程。 使用ThreadPoolExecutor 类的 submit() 方法提交任务,自动创建线程池并执行任务。 创建进程,然后在进程中启动线程。 以上就是python中启

    2024年02月12日
    浏览(45)
  • Java多线程编程中的线程同步

    基本概念: ​ 线程同步是多线程编程中的一个重要概念,用于控制多个线程对共享资源的访问,以防止数据的不一致性和并发问题。 在多线程环境下,多个线程同时访问共享资源可能导致数据的竞争和不正确的结果。 是确保多个线程按照特定的顺序和规则访问共享资源,以

    2024年02月13日
    浏览(47)
  • 【Linux】多线程02 --- 线程的同步互斥问题及生产消费模型

    🍎 作者: 阿润菜菜 📖 专栏: Linux系统编程 线程同步互斥问题是指多线程程序中,如何保证共享资源的正确访问和线程间的协作。 因为线程互斥是实现线程同步的基础和前提,我们先讲解线程互斥问题。 在多线程中,假设我们有一个黄牛抢票的代码,其中有一份共享资源

    2024年02月08日
    浏览(44)
  • 【Java 基础篇】Java多线程编程详解:线程创建、同步、线程池与性能优化

    Java是一门强大的编程语言,其中最引人注目的特性之一是多线程支持。多线程允许我们在同一程序中同时执行多个任务,这大大提高了应用程序的性能和响应能力。本文将深入介绍Java线程的基础知识,无论您是初学者还是有一些经验的开发人员,都将从中获益。 在计算机科

    2024年02月07日
    浏览(57)
  • java多线程怎么同步返回结果

    在 Java 多线程中,如果需要等待线程执行完成并返回结果,可以使用 Java 的线程同步机制来实现。以下是一些常用的方式: 使用 join() 方法:可以使用线程的 join() 方法来等待线程执行完成。在主线程中调用 join() 方法,会阻塞主线程,直到该线程执行完成。在被等待的线程执

    2024年02月14日
    浏览(33)
  • 【Java基础】线程同步类 CountDownLatch

    ​ 关于作者:CSDN内容合伙人、技术专家, 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 ,擅长java后端、移动开发、人工智能等,希望大家多多支持。 正好今天项目中用到了CountDownLatch,那我们正好总结一下,通过本文你可以学到什么是CountDownLatch及其原理,

    2024年02月12日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包