CountDownLatch和wait noftify方法有什么区别
CountDownLatch和wait/notify方法是在多线程编程中用于线程之间的协调和同步的机制,但它们有一些区别。
- 功能和用途:
- CountDownLatch:CountDownLatch是一个计数器,用于等待其他线程完成一组操作。它通过一个初始计数值来初始化,然后在每个线程完成操作时进行递减操作,直到计数值变为0,等待的线程才会被唤醒继续执行。
- wait/notify:wait/notify是Java中对象的内置方法,用于线程之间的等待和唤醒操作。wait方法用于使当前线程等待,直到其他线程通过notify方法唤醒它。notify方法用于唤醒一个等待中的线程。
- 使用方式:
- CountDownLatch:CountDownLatch的使用方式相对简单,通过调用await()方法来等待计数值变为0,通过调用countDown()方法来递减计数值。
- wait/notify:wait/notify是通过在synchronized块中调用wait和notify方法来进行线程的等待和唤醒。通常需要使用共享对象的monitor(锁对象)来进行同步。
- 同步机制:
- CountDownLatch:CountDownLatch是一种单向的同步机制,即主线程等待其他线程完成,但其他线程无法等待主线程完成。
- wait/notify:wait/notify是基于共享对象的同步机制,允许线程之间的相互等待和唤醒。
- 线程之间的关系:
- CountDownLatch:CountDownLatch适用于一个线程(或多个线程)等待其他多个线程的情况。
- wait/notify:wait/notify适用于多个线程之间的相互协作和通信。
综上所述,CountDownLatch和wait/notify方法在功能、使用方式、同步机制和线程之间的关系等方面存在一些区别。CountDownLatch适用于一个线程等待其他多个线程的情况,而wait/notify方法适用于多个线程之间的相互协作和通信。
CountDownLatch和join有什么区别
CountDownLatch和join是多线程编程中用于线程之间的协调和同步的机制,它们有一些区别。
- 使用方式:
- CountDownLatch:CountDownLatch是通过一个计数器来实现线程的等待和唤醒。主线程调用await()方法等待计数器变为0,其他线程通过countDown()方法递减计数器。
- join:join是通过调用线程对象的join()方法实现线程的等待。一个线程调用另一个线程的join()方法,等待该线程执行完成后再继续执行。
- 同步对象:
- CountDownLatch:CountDownLatch使用一个计数器作为同步对象,主线程等待其他线程通过countDown()方法将计数器减为0。
- join:join使用线程对象作为同步对象,一个线程等待另一个线程的执行完成。
- 等待方式:
- CountDownLatch:主线程通过await()方法等待计数器变为0,等待其他线程完成。
- join:一个线程通过调用另一个线程的join()方法等待其执行完成。
- 线程间的关系:
- CountDownLatch:CountDownLatch适用于一个线程等待其他多个线程的情况,主线程等待其他线程的完成。
- join:join适用于一个线程等待另一个线程的完成,一个线程等待指定的线程执行完成。
- 可重用性:
- 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");
}
}
在上述示例中,我们创建了两个线程thread1
和thread2
,它们分别执行一些耗时的操作。在主线程中,我们通过调用thread1.join()
和thread2.join()
方法来等待thread1
和thread2
执行完成。这样,主线程会等待两个子线程执行完毕后再继续执行。最后,主线程输出"Main thread completed"。
注意,join
方法会阻塞调用线程,直到被调用的线程执行完成。在上述示例中,主线程在调用join
方法后会阻塞等待thread1
和thread2
的执行完成,然后才会继续执行后面的代码。
为上述代码添加统计耗时
以下是添加了耗时统计的完整代码示例:
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();
}
}
}
在上述代码中,我们在线程thread1
和thread2
的执行代码中添加了计算耗时的逻辑。在主线程中,我们在调用join
方法前记录了开始时间,然后在join
方法返回后记录了结束时间,从而计算出主线程的耗时。同样地,在线程thread1
和thread2
的执行代码中也记录了各自的开始时间和结束时间,并输出了执行耗时。
这样,我们就可以得到每个线程的执行耗时以及主线程的总体耗时信息。
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();
}
}
在上述代码中,我们创建了两个线程 thread1
和 thread2
,它们共享一个对象 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
)。
这样,我们就可以得到每个线程的执行时间以及总体的执行时间信息。