轩辕李的博客 轩辕李的博客
首页
  • Java
  • Spring
  • 其他语言
  • 工具
  • HTML&CSS
  • JavaScript
  • 分布式
  • 代码质量管理
  • 基础
  • 操作系统
  • 计算机网络
  • 编程范式
  • 安全
  • 中间件
  • 心得
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

轩辕李

勇猛精进,星辰大海
首页
  • Java
  • Spring
  • 其他语言
  • 工具
  • HTML&CSS
  • JavaScript
  • 分布式
  • 代码质量管理
  • 基础
  • 操作系统
  • 计算机网络
  • 编程范式
  • 安全
  • 中间件
  • 心得
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Java

    • 核心

    • 并发

      • Java并发-线程基础与synchronized关键字
      • Java并发-重入锁ReentrantLock详解与实践
      • Java并发-信号量Semaphore
      • Java并发-读写锁ReadWriteLock
      • Java并发-倒计时器CountDownLatch
      • Java并发-栅栏CyclicBarrier
      • Java并发-LockSupport线程阻塞工具类
        • 一、为什么需要 LockSupport
        • 二、基本使用
        • 三、核心方法详解
          • 1、park() 方法族
          • 2、unpark() 方法
        • 四、许可机制(Permit)
          • 1、许可机制示例
        • 五、实际应用场景
          • 1、实现简单的同步器
          • 2、实现生产者消费者模式
          • 3、响应中断的阻塞
        • 六、与其他同步工具的对比
        • 七、使用注意事项
          • 1、许可不会累加
          • 2、park 方法可能无理由返回
          • 3、使用 blocker 参数便于诊断
          • 4、注意内存可见性
        • 八、最佳实践
        • 九、总结
      • Java并发-线程池ThreadPoolExecutor
      • Java并发-阻塞队列BlockingQueue
      • Java并发-以空间换时间之ThreadLocal
      • Java并发-无锁策略CAS与atomic包
      • Java并发-JDK并发容器
      • Java并发-异步调用结果之Future和CompletableFuture
      • Java并发-Fork Join框架
      • Java并发-调试与诊断
    • 经验

    • JVM

    • 企业应用

  • Spring

  • 其他语言

  • 工具

  • 后端
  • Java
  • 并发
轩辕李
2018-06-20
目录

Java并发-LockSupport线程阻塞工具类

LockSupport 是 Java 并发包中一个非常底层且强大的线程阻塞工具类,它为线程的阻塞和唤醒提供了更加灵活和高效的机制。

# 一、为什么需要 LockSupport

在 Java 中,让线程暂停有多种方式:

  • Object.wait() - 需要先获取对象锁,必须在 synchronized 块中使用
  • Thread.sleep() - 让线程睡眠指定时间,不能被其他线程唤醒
  • Thread.suspend() - 已废弃,线程挂起时不释放锁资源,容易导致死锁

这些方法都有各自的限制。LockSupport 提供了一种更加灵活的线程阻塞机制:

  • 不需要获取锁就可以让线程阻塞
  • 可以指定阻塞的线程,精确唤醒
  • 基于许可(permit)机制,避免了死锁问题
  • 是构建同步组件的基础工具(AQS 就是基于它实现的)

# 二、基本使用

先来看一个简单的示例:

public class LockSupportDemo {
	static LsThread t1 = new LsThread("t1");
	static LsThread t2 = new LsThread("t2");
	
	public static void main(String[] args) throws InterruptedException {
		t1.start();
		Thread.sleep(1000);
		t2.start();
		LockSupport.unpark(t1);
		LockSupport.unpark(t2);
		t1.join();
		t2.join();
	}
}

 class LsThread extends Thread{
	 
	public LsThread(String name) {
		super(name);
	}

	@Override
	public void run() {
		System.out.println("in "+getName());
		LockSupport.park(this);
		System.out.println("out " +getName());
	}
	
}

运行结果:

in t1
in t2
out t1
out t2

在这个例子中,t1 和 t2 线程启动后都会调用 park() 方法暂停,主线程通过 unpark() 方法唤醒它们。

# 三、核心方法详解

# 1、park() 方法族

// 阻塞当前线程,如果有许可则消耗许可并立即返回
public static void park()

// 阻塞当前线程,传入 blocker 对象用于监控和诊断
public static void park(Object blocker)

// 阻塞当前线程,最长等待 nanos 纳秒
public static void parkNanos(long nanos)

// 阻塞当前线程,直到 deadline 时间点
public static void parkUntil(long deadline)

# 2、unpark() 方法

// 给指定线程一个许可,如果线程已阻塞则唤醒它
public static void unpark(Thread thread)

# 四、许可机制(Permit)

LockSupport 的核心是基于许可(permit)的机制:

  • 每个线程都有一个许可,初始值为 0
  • 调用 unpark() 方法会将许可设置为 1(多次调用不会累加)
  • 调用 park() 方法时:
    • 如果许可为 1,则消耗许可(设为 0)并立即返回
    • 如果许可为 0,则线程阻塞

# 1、许可机制示例

public class PermitDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            System.out.println("线程开始执行");
            
            // 先 unpark,获得许可
            LockSupport.unpark(Thread.currentThread());
            
            // 调用 park,因为有许可,所以不会阻塞
            LockSupport.park();
            System.out.println("第一次 park 后继续执行");
            
            // 再次调用 park,没有许可了,会阻塞
            LockSupport.park();
            System.out.println("第二次 park 后继续执行");
        });
        
        thread.start();
        Thread.sleep(2000);
        
        // 唤醒阻塞的线程
        LockSupport.unpark(thread);
        thread.join();
    }
}

# 五、实际应用场景

# 1、实现简单的同步器

public class SimpleLatch {
    private volatile boolean released = false;
    
    public void await() {
        while (!released) {
            LockSupport.park(this);
        }
    }
    
    public void release() {
        released = true;
        // 唤醒所有等待的线程
        for (Thread thread : Thread.getAllStackTraces().keySet()) {
            LockSupport.unpark(thread);
        }
    }
}

# 2、实现生产者消费者模式

public class ProducerConsumer {
    private final Queue<String> queue = new LinkedList<>();
    private final int capacity = 5;
    private Thread consumer;
    private Thread producer;
    
    public void produce(String item) {
        synchronized (queue) {
            while (queue.size() >= capacity) {
                LockSupport.park(this);
            }
            queue.offer(item);
            System.out.println("生产: " + item);
        }
        // 唤醒消费者
        LockSupport.unpark(consumer);
    }
    
    public String consume() {
        String item;
        synchronized (queue) {
            while (queue.isEmpty()) {
                LockSupport.park(this);
            }
            item = queue.poll();
            System.out.println("消费: " + item);
        }
        // 唤醒生产者
        LockSupport.unpark(producer);
        return item;
    }
}

# 3、响应中断的阻塞

public class InterruptiblePark {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            System.out.println("线程开始 park");
            LockSupport.park();
            
            // park 方法响应中断但不抛出异常
            if (Thread.interrupted()) {
                System.out.println("线程被中断了");
            }
            System.out.println("线程继续执行");
        });
        
        thread.start();
        Thread.sleep(1000);
        
        // 中断线程,park 会响应中断
        thread.interrupt();
        thread.join();
    }
}

# 六、与其他同步工具的对比

特性 LockSupport Object.wait() Thread.sleep() Condition.await()
需要获取锁 否 是 否 是
可以响应中断 是(不抛异常) 是(抛异常) 是(抛异常) 是(抛异常)
可以设置超时 是 是 是 是
精确唤醒 是 否(notifyAll) 不支持唤醒 是
使用复杂度 简单 中等 简单 复杂

# 七、使用注意事项

# 1、许可不会累加

// 多次 unpark 只会给一个许可
LockSupport.unpark(thread);
LockSupport.unpark(thread);  // 第二次调用无效

// 线程只能消耗一个许可
LockSupport.park();  // 消耗许可
LockSupport.park();  // 会阻塞

# 2、park 方法可能无理由返回

// park 可能会"虚假唤醒",应该在循环中使用
while (!condition) {
    LockSupport.park(this);
}

# 3、使用 blocker 参数便于诊断

// 推荐传入 blocker 对象,便于通过诊断工具查看
LockSupport.park(this);  // 好
LockSupport.park();      // 不推荐

# 4、注意内存可见性

public class VisibilityDemo {
    // 使用 volatile 确保可见性
    private volatile boolean flag = false;
    
    public void waitForFlag() {
        while (!flag) {
            LockSupport.park(this);
        }
    }
    
    public void setFlag() {
        flag = true;
        // 唤醒等待的线程
        LockSupport.unpark(waitingThread);
    }
}

# 八、最佳实践

  1. 优先使用高层同步工具:如 CountDownLatch、Semaphore 等,它们基于 LockSupport 实现但提供了更高级的抽象

  2. 在循环中检查条件:避免虚假唤醒导致的问题

  3. 合理使用 blocker 参数:有助于问题诊断和调试

  4. 注意线程中断状态:park 响应中断但不清除中断状态

  5. 配合 volatile 使用:确保状态的内存可见性

# 九、总结

LockSupport 是 Java 并发包的基础工具,它提供了灵活、高效的线程阻塞和唤醒机制。虽然它很强大,但在实际开发中,我们通常使用基于它构建的更高层次的同步工具。理解 LockSupport 的工作原理有助于我们更好地理解和使用 Java 并发包中的其他组件。

编辑 (opens new window)
#LockSupport
上次更新: 2025/08/14
Java并发-栅栏CyclicBarrier
Java并发-线程池ThreadPoolExecutor

← Java并发-栅栏CyclicBarrier Java并发-线程池ThreadPoolExecutor→

最近更新
01
AI时代的编程心得
09-11
02
Claude Code与Codex的协同工作
09-01
03
Claude Code实战之供应商切换工具
08-18
更多文章>
Theme by Vdoing | Copyright © 2018-2025 京ICP备2021021832号-2 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式