Java基础: CountDownLatch和wait noftify方法、join方法有什么区别

CountDownLatch和wait noftify方法有什么区别

CountDownLatch和wait/notify方法是在多线程编程中用于线程之间的协调和同步的机制,但它们有一些区别。

  1. 功能和用途:

    • CountDownLatch:CountDownLatch是一个计数器,用于等待其他线程完成一组操作。它通过一个初始计数值来初始化,然后在每个线程完成操作时进行递减操作,直到计数值变为0,等待的线程才会被唤醒继续执行。
    • wait/notify:wait/notify是Java中对象的内置方法,用于线程之间的等待和唤醒操作。wait方法用于使当前线程等待,直到其他线程通过notify方法唤醒它。notify方法用于唤醒一个等待中的线程。
  2. 使用方式:

    • CountDownLatch:CountDownLatch的使用方式相对简单,通过调用await()方法来等待计数值变为0,通过调用countDown()方法来递减计数值。
    • wait/notify:wait/notify是通过在synchronized块中调用wait和notify方法来进行线程的等待和唤醒。通常需要使用共享对象的monitor(锁对象)来进行同步。
  3. 同步机制:

    • CountDownLatch:CountDownLatch是一种单向的同步机制,即主线程等待其他线程完成,但其他线程无法等待主线程完成。
    • wait/notify:wait/notify是基于共享对象的同步机制,允许线程之间的相互等待和唤醒。
  4. 线程之间的关系:

    • CountDownLatch:CountDownLatch适用于一个线程(或多个线程)等待其他多个线程的情况。
    • wait/notify:wait/notify适用于多个线程之间的相互协作和通信。

综上所述,CountDownLatch和wait/notify方法在功能、使用方式、同步机制和线程之间的关系等方面存在一些区别。CountDownLatch适用于一个线程等待其他多个线程的情况,而wait/notify方法适用于多个线程之间的相互协作和通信。

CountDownLatch和join有什么区别

CountDownLatch和join是多线程编程中用于线程之间的协调和同步的机制,它们有一些区别。

  1. 使用方式:

    • CountDownLatch:CountDownLatch是通过一个计数器来实现线程的等待和唤醒。主线程调用await()方法等待计数器变为0,其他线程通过countDown()方法递减计数器。
    • join:join是通过调用线程对象的join()方法实现线程的等待。一个线程调用另一个线程的join()方法,等待该线程执行完成后再继续执行。
  2. 同步对象:

    • CountDownLatch:CountDownLatch使用一个计数器作为同步对象,主线程等待其他线程通过countDown()方法将计数器减为0。
    • join:join使用线程对象作为同步对象,一个线程等待另一个线程的执行完成。
  3. 等待方式:

    • CountDownLatch:主线程通过await()方法等待计数器变为0,等待其他线程完成。
    • join:一个线程通过调用另一个线程的join()方法等待其执行完成。
  4. 线程间的关系:

    • CountDownLatch:CountDownLatch适用于一个线程等待其他多个线程的情况,主线程等待其他线程的完成。
    • join:join适用于一个线程等待另一个线程的完成,一个线程等待指定的线程执行完成。
  5. 可重用性:

    • CountDownLatch:CountDownLatch的计数器只能减少,一旦计数器变为0,无法重置。
    • join:join方法可以在一个线程执行完成后重新使用,等待同一个线程的多次执行。

综上所述,CountDownLatch和join在使用方式、同步对象、等待方式、线程间关系和可重用性等方面存在一些区别。CountDownLatch适用于一个线程等待其他多个线程的情况,通过计数器进行等待和唤醒。join适用于一个线程等待另一个线程的完成,通过调用线程对象的join()方法进行等待。

join代码示例

下面是一个使用join方法的示例代码:

public class JoinExample {
    
    
    public static void main(String[] args) {
    
    
        Thread thread1 = new Thread(() -> {
    
    
            System.out.println("Thread 1 started");
            try {
    
    
                Thread.sleep(2000); // 模拟线程1的耗时操作
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            System.out.println("Thread 1 completed");
        });

        Thread thread2 = new Thread(() -> {
    
    
            System.out.println("Thread 2 started");
            try {
    
    
                Thread.sleep(3000); // 模拟线程2的耗时操作
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            System.out.println("Thread 2 completed");
        });

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

        try {
    
    
            System.out.println("Main thread waiting for thread 1 and thread 2 to complete");
            thread1.join(); // 等待线程1执行完成
            thread2.join(); // 等待线程2执行完成
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }

        System.out.println("Main thread completed");
    }
}

在上述示例中,我们创建了两个线程thread1thread2,它们分别执行一些耗时的操作。在主线程中,我们通过调用thread1.join()thread2.join()方法来等待thread1thread2执行完成。这样,主线程会等待两个子线程执行完毕后再继续执行。最后,主线程输出"Main thread completed"。

注意,join方法会阻塞调用线程,直到被调用的线程执行完成。在上述示例中,主线程在调用join方法后会阻塞等待thread1thread2的执行完成,然后才会继续执行后面的代码。

为上述代码添加统计耗时

以下是添加了耗时统计的完整代码示例:

public class JoinExample {
    
    
    public static void main(String[] args) {
    
    
        Thread thread1 = new Thread(() -> {
    
    
            System.out.println("Thread 1 started");
            long startTime = System.currentTimeMillis();
            try {
    
    
                Thread.sleep(2000); // 模拟线程1的耗时操作
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            long endTime = System.currentTimeMillis();
            System.out.println("Thread 1 completed in " + (endTime - startTime) + " milliseconds");
        });

        Thread thread2 = new Thread(() -> {
    
    
            System.out.println("Thread 2 started");
            long startTime = System.currentTimeMillis();
            try {
    
    
                Thread.sleep(3000); // 模拟线程2的耗时操作
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            long endTime = System.currentTimeMillis();
            System.out.println("Thread 2 completed in " + (endTime - startTime) + " milliseconds");
        });

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

        try {
    
    
            System.out.println("Main thread waiting for thread 1 and thread 2 to complete");
            long startTime = System.currentTimeMillis();
            thread1.join(); // 等待线程1执行完成
            thread2.join(); // 等待线程2执行完成
            long endTime = System.currentTimeMillis();
            System.out.println("Main thread completed in " + (endTime - startTime) + " milliseconds");
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
    }
}

在上述代码中,我们在线程thread1thread2的执行代码中添加了计算耗时的逻辑。在主线程中,我们在调用join方法前记录了开始时间,然后在join方法返回后记录了结束时间,从而计算出主线程的耗时。同样地,在线程thread1thread2的执行代码中也记录了各自的开始时间和结束时间,并输出了执行耗时。

这样,我们就可以得到每个线程的执行耗时以及主线程的总体耗时信息。

wait notify代码示例

以下是使用 wait()notify() 方法的代码示例:

public class WaitNotifyExample {
    
    
    public static void main(String[] args) {
    
    
        final Object lock = new Object(); // 创建一个共享的对象作为锁

        Thread thread1 = new Thread(() -> {
    
    
            synchronized (lock) {
    
    
                try {
    
    
                    System.out.println("Thread 1 started");
                    lock.wait(); // 线程1等待
                    System.out.println("Thread 1 resumed");
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            }
        });

        Thread thread2 = new Thread(() -> {
    
    
            synchronized (lock) {
    
    
                System.out.println("Thread 2 started");
                lock.notify(); // 唤醒等待中的线程1
                System.out.println("Thread 2 completed");
            }
        });

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

在上述代码中,我们创建了两个线程 thread1thread2,它们共享一个对象 lock 作为锁。在线程 thread1 中,我们使用 synchronized 关键字获取 lock 锁,并在 lock.wait() 处调用 wait() 方法使线程进入等待状态。线程 thread2 中,我们同样使用 synchronized 关键字获取 lock 锁,并在 lock.notify() 处调用 notify() 方法唤醒等待中的线程。这样,线程 thread1 将被唤醒并继续执行。

请注意,在使用 wait()notify() 方法时,必须在同步块中调用它们,并且必须获取相同的锁对象。这样才能确保线程之间的正确通信和同步。

CountDownLatch代码示例

以下是使用 CountDownLatch 的代码示例:

import java.util.concurrent.CountDownLatch;

public class CountDownLatchExample {
    
    
    public static void main(String[] args) {
    
    
        int numThreads = 3; // 线程数量

        CountDownLatch latch = new CountDownLatch(numThreads); // 创建一个计数器,初始计数值为线程数量

        for (int i = 0; i < numThreads; i++) {
    
    
            Thread thread = new Thread(() -> {
    
    
                System.out.println("Thread started");
                // 模拟线程执行耗时操作
                try {
    
    
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
                System.out.println("Thread completed");
                latch.countDown(); // 计数器减一
            });
            thread.start();
        }

        try {
    
    
            System.out.println("Main thread waiting for all threads to complete");
            latch.await(); // 等待计数器变为0
            System.out.println("All threads completed");
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
    }
}

在上述代码中,我们创建了一个 CountDownLatch 对象,并将线程数量作为初始计数值传入。然后,我们创建了多个线程,在每个线程的执行逻辑中,首先输出 “Thread started”,然后模拟线程执行耗时操作,最后输出 “Thread completed”,并调用 countDown() 方法将计数器减一。

在主线程中,我们调用 latch.await() 方法来等待计数器变为0。这将使主线程阻塞,直到所有线程完成其工作并将计数器减为0。最后,主线程输出 “All threads completed”。

CountDownLatch 的作用是在主线程中等待其他线程完成特定操作后再继续执行。通过适当地使用 CountDownLatch,我们可以实现线程之间的协调和同步。

为上述代码加入耗时统计

以下是在示例代码中加入耗时统计的修改版本:

package com.zxl.day_20230603;

import java.util.concurrent.CountDownLatch;

import java.util.concurrent.CountDownLatch;

public class CountDownLatchExample {
    
    
    public static void main(String[] args) {
    
    
        int numThreads = 3; // 线程数量

        CountDownLatch latch = new CountDownLatch(numThreads); // 创建一个计数器,初始计数值为线程数量

        for (int i = 0; i < numThreads; i++) {
    
    
            Thread thread = new Thread(() -> {
    
    
                System.out.println("Thread started");
                long threadStartTime = System.currentTimeMillis(); // 记录线程开始时间
                // 模拟线程执行耗时操作
                try {
    
    
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
                long threadEndTime = System.currentTimeMillis(); // 记录线程结束时间
                System.out.println("Thread completed");
                System.out.println("Thread execution time: " + (threadEndTime - threadStartTime) + " milliseconds");
                latch.countDown(); // 计数器减一
            });
            thread.start();
        }

        try {
    
    
            System.out.println("Main thread waiting for all threads to complete");
            long startTime = System.currentTimeMillis(); // 记录开始时间
            latch.await(); // 等待计数器变为0
            long endTime = System.currentTimeMillis(); // 记录结束时间
            System.out.println("Total execution time: " + (endTime - startTime) + " milliseconds");
            System.out.println("All threads completed");
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
    }
}

在修改后的代码中,我们在主线程中添加了开始时间的记录(startTime),在每个线程的执行逻辑中添加了线程开始时间的记录(threadStartTime)和线程结束时间的记录(threadEndTime)。在每个线程完成后,我们输出线程的执行时间(threadEndTime - threadStartTime)。

在主线程中,我们在所有线程完成后,记录了结束时间(endTime),并输出总体执行时间(endTime - startTime)。

这样,我们就可以得到每个线程的执行时间以及总体的执行时间信息。

猜你喜欢

转载自blog.csdn.net/a772304419/article/details/131022680