Java 中的线程同步方式令人眼花撩乱,JDK提供的工具与方法学习与使用起来容易混淆,本文通过Object、Thread、Lock、Lock Support几个类类帮你梳理这些概念,希望对你有帮助。
Object
wait,notify
public class Example {
public synchronized void waitForCondition() {
while (!condition) {
try {
wait(); // 释放锁并等待
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
// 条件满足,执行操作
}
public synchronized void changeCondition() {
// 改变条件
condition = true;
notify(); // 唤醒等待的线程
}
}
Thread
sleep、yield
public static void main(String[] args) {
Runnable runnable = () -> {
for (int i = 0; i <= 100; i++) {
System.out.println(Thread.currentThread().getName() + "-----" + i);
if (i % 20 == 0) {
Thread.yield();
}
}
};
new Thread(runnable, "栈长").start();
new Thread(runnable, "小蜜").start();
}
Lock
await、lock
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
lock.lock();
try {
while (conditionNotMet) {
condition.await();
}
// 条件已满足,执行操作
} finally {
lock.unlock();
}
LockSupport
park
public class LockSupportExample {
public static void main(String[] args) {
final Thread threadA = new Thread(() -> {
System.out.println("线程A等待信号");
LockSupport.park(); // 线程A停下来等待
System.out.println("线程A收到信号");
});
final Thread threadB = new Thread(() -> {
System.out.println("线程B发送信号");
LockSupport.unpark(threadA); // 唤醒线程A
});
threadA.start();
threadB.start();
}
}
如何唤醒?
- sleep:
- sleep() 是 Thread 类的一个静态方法,它使当前线程暂停执行指定的时间长度,让出CPU给其他线程。
- sleep() 方法不会释放任何锁,因此它不会影响其他线程对锁的获取。
- 由于 sleep() 不会释放锁,所以它不能被 notify` 或 notifyAll唤醒,因为这些方法与锁有关。
- wait:
- wait()是Object类的一个实例方法,它使当前线程等待,直到它被另一个线程通过调用同一个对象的 notify 或 notifyAll方法唤醒。
- 当线程调用 wait() 时,它会释放当前对象的锁,并进入等待状态。
- wait() 必须在同步方法或同步块中调用。
- await:
- await()是 java.util.concurrent.locks.Condition 接口的一个方法,通常与显式的锁(如 ReentrantLock)一起使用。
- 当线程调用 await()时,它会释放与条件相关的锁,并进入等待状态。
- await() 可以被相同Condition 对象上的 signal 或 signalAll方法唤醒。
- park:
- park() 并不是传统意义上的锁。它不会去竞争什么资源,只是纯粹地阻塞线程。而且,它还有一个非常酷的特性——不易产生死锁。因为park() 在等待过程中,如果接收到了unpark()的信号,它会立刻返回,这就避免了像synchronized 那样容易陷入死锁的问题。
- yield:
- yield() 的调用并不保证当前线程一定会被挂起或暂停执行,也不保证线程调度器会立即调度其他线程来运行。它仅仅是一个提示,实际的行为取决于线程调度器的实现和当前系统的线程状态。
- yield() 本身并不涉及线程间的等待和唤醒机制,它并不需要被其他线程唤醒。当一个线程调用 yield()后,如果线程调度器决定接受这个提示,当前线程可能会被暂停执行,其他线程(如果有的话)可能会被调度来运行。如果线程调度器决定不接受这个提示,当前线程可能不会立即放弃CPU时间片,而是继续执行。
参考文档:
多线程 Thread.yield 方法到底有什么用:https://blog.csdn.net/youanyyou/article/details/84282668
深入理解Lock Support:https://zhuanlan.zhihu.com/p/677641685
JDK1.8源码分析之LockSupport(一):https://www.cnblogs.com/leesf456/p/5347293.html