Java basics: What is the difference between CountDownLatch and wait notify method and join method

What is the difference between CountDownLatch and wait notify methods

CountDownLatch and wait/notify methods are mechanisms used in multithreaded programming for coordination and synchronization between threads, but they have some differences.

  1. Function and use:

    • CountDownLatch: CountDownLatch is a counter that waits for other threads to complete a set of operations. It is initialized with an initial count value, and then decremented when each thread completes the operation, until the count value becomes 0, the waiting thread will be woken up to continue execution.
    • wait/notify: wait/notify is a built-in method of objects in Java, which is used for waiting and waking operations between threads. The wait method is used to make the current thread wait until other threads wake it up through the notify method. The notify method is used to wake up a waiting thread.
  2. How to use:

    • CountDownLatch: The use of CountDownLatch is relatively simple, by calling the await() method to wait for the count value to become 0, and by calling the countDown() method to decrement the count value.
    • wait/notify: wait/notify is to wait and wake up the thread by calling the wait and notify methods in the synchronized block. It is usually necessary to use the monitor (lock object) of the shared object for synchronization.
  3. Synchronization mechanism:

    • CountDownLatch: CountDownLatch is a one-way synchronization mechanism, that is, the main thread waits for other threads to complete, but other threads cannot wait for the main thread to complete.
    • wait/notify: wait/notify is a synchronization mechanism based on shared objects, allowing threads to wait and wake up each other.
  4. Relationship between threads:

    • CountDownLatch: CountDownLatch is suitable for situations where one thread (or multiple threads) waits for other multiple threads.
    • wait/notify: wait/notify is suitable for mutual cooperation and communication between multiple threads.

To sum up, there are some differences between CountDownLatch and wait/notify methods in terms of function, usage, synchronization mechanism and relationship between threads. CountDownLatch is suitable for a thread waiting for other multiple threads, and the wait/notify method is suitable for mutual cooperation and communication between multiple threads.

What is the difference between CountDownLatch and join

CountDownLatch and join are mechanisms for coordination and synchronization between threads in multithreaded programming, and they have some differences.

  1. How to use:

    • CountDownLatch: CountDownLatch implements thread waiting and wakeup through a counter. The main thread calls the await() method to wait for the counter to become 0, and other threads decrement the counter through the countDown() method.
    • join: join is to realize the thread waiting by calling the join() method of the thread object. A thread calls the join() method of another thread, and waits for the thread to complete before continuing.
  2. Sync object:

    • CountDownLatch: CountDownLatch uses a counter as a synchronization object, and the main thread waits for other threads to reduce the counter to 0 through the countDown() method.
    • join: join uses a thread object as a synchronization object, and one thread waits for the execution of another thread to complete.
  3. Waiting method:

    • CountDownLatch: The main thread waits for the counter to become 0 through the await() method, and waits for other threads to complete.
    • join: A thread waits for its execution to complete by calling the join() method of another thread.
  4. Relationship between threads:

    • CountDownLatch: CountDownLatch is suitable for situations where one thread waits for other multiple threads, and the main thread waits for the completion of other threads.
    • join: join is suitable for one thread to wait for the completion of another thread, and one thread to wait for the execution of the specified thread to complete.
  5. Reusability:

    • CountDownLatch: The counter of CountDownLatch can only be decreased, once the counter becomes 0, it cannot be reset.
    • join: The join method can be reused after a thread executes, waiting for multiple executions of the same thread.

To sum up, there are some differences between CountDownLatch and join in terms of usage, synchronization objects, waiting methods, inter-thread relationships, and reusability. CountDownLatch applies to the situation where a thread waits for other multiple threads, and waits and wakes up through the counter. Join is suitable for one thread to wait for the completion of another thread, by calling the join() method of the thread object to wait.

join code example

Here is a joinsample code of how to use it:

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");
    }
}

In the above example, we created two threads thread1and thread2they each perform some time-consuming operations. In the main thread, we wait for and execute completion by calling thread1.join()and thread2.join()methods . In this way, the main thread will wait for the two sub-threads to finish executing before continuing. Finally, the main thread outputs "Main thread completed".thread1thread2

Note that jointhe method blocks the calling thread until the called thread finishes executing. In the above example, after calling jointhe method, the main thread will block thread1and wait thread2for the execution of the sum to complete before continuing to execute the following code.

Add statistical time-consuming to the above code

Here is a complete code example with time-consuming statistics added:

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();
        }
    }
}

In the above code, we added time-consuming logic to the execution code of threads thread1and sums. thread2In the main thread, we joinrecord the start time before calling the method, and then joinrecord the end time after the method returns to calculate the time spent on the main thread. Similarly, the respective start time and end time are also recorded in the execution code of thread thread1and , and the execution time is output.thread2

In this way, we can get the execution time of each thread and the overall time consumption of the main thread.

wait notify code example

Here wait()is notify()a code example using the and methods:

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();
    }
}

In the above code, we create two threads thread1and thread2, which share an object lockas a lock. thread1In the thread , we use synchronizedthe keyword to acquire lockthe lock , and lock.wait()call wait()the method at to make the thread enter the waiting state. thread2In thread , we also use synchronizedthe keyword to acquire lockthe lock , and lock.notify()call notify()the method to wake up the waiting thread. In this way, the thread thread1will wake up and continue executing.

Note that when using the wait()and notify()methods, they must be called within a synchronized block and must acquire the same lock object. This ensures proper communication and synchronization between threads.

CountDownLatch code example

Here is a code example CountDownLatchusing :

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();
        }
    }
}

In the above code, we create a CountDownLatchobject and pass in the number of threads as the initial count value. Then, we created multiple threads. In the execution logic of each thread, first output "Thread started", then simulate the thread to perform time-consuming operations, and finally output "Thread completed", and call countDown()the method

In the main thread, we call latch.await()the method to wait for the counter to become 0. This will block the main thread until all threads finish their work and decrement the counter to 0. Finally, the main thread outputs "All threads completed".

CountDownLatchThe function is to wait for other threads to complete specific operations in the main thread before continuing. With proper use CountDownLatch, we can achieve coordination and synchronization between threads.

Add time-consuming statistics to the above code

The following is a modified version that adds time-consuming statistics to the sample code:

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();
        }
    }
}

In the modified code, we added the record of the start time ( startTime) in the main thread, and added the record of the thread start time ( threadStartTime) and the record of the thread end time ( threadEndTime) in the execution logic of each thread. After each thread finishes, we output the thread's execution time ( threadEndTime - threadStartTime).

In the main thread, we log the end time ( endTime) and output the overall execution time ( endTime - startTime) after all threads have finished.

In this way, we can get the execution time of each thread and the overall execution time information.

Guess you like

Origin blog.csdn.net/a772304419/article/details/131022680