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

如何唤醒?

  1. sleep:
    • sleep() 是 Thread 类的一个静态方法,它使当前线程暂停执行指定的时间长度,让出CPU给其他线程。
    • sleep() 方法不会释放任何锁,因此它不会影响其他线程对锁的获取。
    • 由于 sleep() 不会释放锁,所以它不能被 notify` 或 notifyAll唤醒,因为这些方法与锁有关。
  2. wait:
    • wait()是Object类的一个实例方法,它使当前线程等待,直到它被另一个线程通过调用同一个对象的 notify 或 notifyAll方法唤醒。
    • 当线程调用 wait() 时,它会释放当前对象的锁,并进入等待状态。
    • wait() 必须在同步方法或同步块中调用。
  3. await:
    • await()是 java.util.concurrent.locks.Condition 接口的一个方法,通常与显式的锁(如 ReentrantLock)一起使用。
    • 当线程调用 await()时,它会释放与条件相关的锁,并进入等待状态。
    • await() 可以被相同Condition 对象上的 signal 或 signalAll方法唤醒。
  4. park:
    • park() 并不是传统意义上的锁。它不会去竞争什么资源,只是纯粹地阻塞线程。而且,它还有一个非常酷的特性——不易产生死锁。因为park() 在等待过程中,如果接收到了unpark()的信号,它会立刻返回,这就避免了像synchronized 那样容易陷入死锁的问题。
  5. 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