探索Java并发编程利器:LockSupport,一种高效的线程阻塞与唤醒机制

这篇具有很好参考价值的文章主要介绍了探索Java并发编程利器:LockSupport,一种高效的线程阻塞与唤醒机制。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

关于作者:CSDN内容合伙人、技术专家, 从零开始做日活千万级APP。
专注于分享各领域原创系列文章 ,擅长java后端、移动开发、人工智能等,希望大家多多支持。

探索Java并发编程利器:LockSupport,一种高效的线程阻塞与唤醒机制,java学习之路,java,android,面试,线程,并发

一、导读

我们继续总结学习Java基础知识,温故知新。

二、概览

LockSupport 是 Java SE 9 及以上版本中引入的一个线程同步工具类,用于支持同步方法,提供了多种同步机制.

LockSupport 所有的方法都是静态方法,可以让线程在任意位置阻塞,阻塞之后也有对应的唤醒方法。
LockSupport 底层调用的是Unsafe中的native代码。

java.util.concurrent并发包下很多并发类的底层加锁都是基于LockSupport,如ReentrantLock、CountDownLatch、ParkableLazyDeque 等。

三、用法

LockSupport 类中最常用的方法是 Park() 和 Unpark() 方法,分别用于阻塞和唤醒线程。


       Thread a = new Thread(() -> {

           System.out.println("start " + Thread.currentThread().getName());
           int resule = 0;
           for(int i = 0; i < 10; i ++) {
               resule += i;
           }
           // 阻塞线程
           LockSupport.park();
           System.out.println(resule);

       }, "a");

       a.start();
       try {
//            TimeUnit.MILLISECONDS.sleep(1000);
       } catch (Exception e) {
       }
       
       // 释放,这里不调用的话,上面将一直锁定
       LockSupport.unpark(a);

四、原理

park和unpark都是直接调用了Unsafe的方法,
我们这么理解:这两个方法的原理是基于一个计数器,计数器的值决定了线程的阻塞等待状态。当计数器的值减为 0 时,表示线程可以被唤醒,执行相应的操作。

此外,LockSupport 还提供了一些同步方法,如 Lock 和 Unlock() 方法,用于获取和释放锁。Lock 方法用于获取锁,Unlock() 方法用于释放锁。与 synchronized 关键字不同,Lock 和 Unlock() 方法不需要同时进行,可以在任意一个方法中获取锁,并且可以在任意一个方法中释放锁,因此更加灵活和方便。


    调用park后,此时线程处于waiting的状态
    
    public static void park() {
        UNSAFE.park(false, 0L);
    }
    
    public static void park(Object blocker) {
        // 获取当前线程
        Thread t = Thread.currentThread();
        
        // 设置Blocker
        setBlocker(t, blocker);
        
        // 此后,当前线程就已经阻塞了,后面的函数无法运行,直至unpark函数被调用
        UNSAFE.park(false, 0L);
        
        //unpark函数被调用后,继续执行,如果没有第二个setBlocker,那么之后没有调用park(Object blocker),
        //而直接调用getBlocker函数,得到的还是前一个park(Object blocker)设置的blocker,显然是不符合逻辑的
        setBlocker(t, null);
    }
    
    public static void unpark(Thread thread) {
        if (thread != null)
           UNSAFE.unpark(thread);
    }

Unsafe.java


    public native void unpark(Object var1);

    public native void park(boolean var1, long var2);

五、线程等待和唤醒的方法

5.1 LockSupport.park()

    // Hotspot implementation via intrinsics API
    private static final sun.misc.Unsafe UNSAFE;
    
    static {
        try {
            UNSAFE = sun.misc.Unsafe.getUnsafe();
            
        } catch (Exception ex) { throw new Error(ex); }
    }

Thread.sleep()和LockSupport.park()的区别

Thread.sleep()和LockSupport.park() 都是阻塞当前线程的执行,
都 不会 释放 当前线程占有的锁资源;

  • Thread.sleep()没法从外部唤醒,只能自己醒过来;本身就是一个native方法;
  • LockSupport.park()方法可以被另一个线程调用LockSupport.unpark()方法唤醒;* 底层是调用的Unsafe的native方法;

Thread.sleep()方法需要捕获中断异常。
LockSupport.park()不需要捕获中断异常。

5.2 Object中的wait()、notify、notifyAll

Object类中的wait、notify、notifyAll用于线程等待和唤醒的方法,都必须在sychronized内部执行

Object.wait()原理

在 Object.wait() 方法中,首先使用 synchronized 关键字锁定当前对象,然后调用 Object.wait() 方法等待另一个线程执行完毕并返回一个 Future 对象。在等待过程中,当前线程会一直阻塞,直到另一个线程执行完毕并返回一个 Future 对象。当等待线程执行完毕后,Future 对象的 get() 方法将返回等待线程的结果。因此,在使用 Object.wait() 方法时,需要确保等待线程已经执行完毕,否则可能会导致死锁等问题。

5.3 Condition的await()方法

Java中的Condition是java.util.concurrent.locks包下的一个接口,它主要用于在多线程环境中控制线程的等待和唤醒。

线程需要获得并持有锁,必须在锁块(synchronized或lock中)
必须要先等待后唤醒,线程才能够被唤醒。

5.4 Thread.sleep()和Object.wait()的区别

Thread.sleep()不会释放锁资源,到时间了会自动唤醒,然后继续执行;
Object.wait()会释放锁资源。需要另一个线程使用Object.notify()唤醒

5.5 Object.wait()和LockSupport.park()的区别

二者都会阻塞当前线程的运行。

  • Object.wait()方法需要在synchronized块中执行;

  • LockSupport.park()可以在任意地方执行;

  • Object.wait()方法需要捕获中断异常。

  • LockSupport.park()不需要捕获中断异常。

  • Object.wait()不带超时的,需要另一个线程执行notify()来唤醒,但不一定继续执行后续内容;

  • LockSupport.park()不带超时的,需要另一个线程执行unpark()来唤醒,一定会继续执行后续内容;

5.6 Object.wait()和Condition.await()的区别

Object.wait()和Condition.await()的原理是基本一致的,不同的是Condition.await()底层是调用LockSupport.park()来实现阻塞当前线程的。

5.7 java中,notify/wait等方法为什么要在同步代码块中执行

同步代码块通常由 synchronized 关键字修饰
在Java中,notify() 和 wait() 方法通常用于实现线程之间的通信和同步。这些方法通常在同步代码块中执行,以确保线程安全和原子性。
在同步代码块中执行 notify() 和 wait() 方法可以确保线程安全和原子性,因为它们可以保证在同一时刻只有一个线程可以执行这些方法。如果在非同步代码块中执行这些方法,则可能会导致竞争条件和死锁等问题。

5.8 如果在wait()之前执行了notify()会怎样?

如果当前的线程不是此对象锁的所有者,却调用该对象的notify()或wait()方法时抛出IllegalMonitorStateException异常;
如果当前线程是此对象锁的所有者,wait()将一直阻塞,因为后续将没有其它notify()唤醒它。

5.8 如果在park()之前执行了unpark()会怎样?

线程不会被阻塞,直接跳过park(),继续执行后续内容,大家可以自己尝试下


        Thread a = new Thread(() -> {

            System.out.println("start " + Thread.currentThread().getName());
            int resule = 0;
            for(int i = 0; i < 10; i ++) {
                resule += i;
            }
            LockSupport.park();
            System.out.println(resule);

        }, "a");

        try {
//            TimeUnit.MILLISECONDS.sleep(1000);
        } catch (Exception e) {
        }
        LockSupport.unpark(a);
        a.start();
        LockSupport.unpark(a);

六、 推荐阅读

Java 专栏

SQL 专栏

数据结构与算法

Android学习专栏文章来源地址https://www.toymoban.com/news/detail-602135.html

到了这里,关于探索Java并发编程利器:LockSupport,一种高效的线程阻塞与唤醒机制的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Java并发编程面试题——线程池

    参考文章: 《Java 并发编程的艺术》 7000 字 + 24 张图带你彻底弄懂线程池 (1) 线程池 (ThreadPool) 是一种用于 管理和复用线程的机制 ,它是在程序启动时就预先创建一定数量的线程,将这些线程放入一个池中,并对它们进行有效的管理和复用,从而在需要执行任务时,可以从

    2024年02月07日
    浏览(48)
  • Java并发编程之线程池详解

    目录 🐳今日良言:不悲伤 不彷徨 有风听风 有雨看雨 🐇一、简介 🐇二、相关代码 🐼1.线程池代码 🐼2.自定义实现线程池 🐇三、ThreadPoolExecutor类 首先来介绍一下什么是线程池,线程池是一种利用池化技术思想来实现的线程管理技术,主要是为了复用线程、便利地管理线程

    2024年02月12日
    浏览(43)
  • 【Java 并发编程】一文读懂线程、协程、守护线程

    在 Java 线程的生命周期一文中提到了 就绪状态的线程在获得 CPU 时间片后变为运行中状态 ,否则就会在 可运行状态 或者 阻塞状态 ,那么系统是如何分配线程时间片以及实现线程的调度的呢?下面我们就来讲讲线程的调度策略。 线程调度是指系统为线程分配 CPU 执行时间片

    2023年04月08日
    浏览(58)
  • 【Java并发编程】变量的线程安全分析

    1.成员变量和静态变量是否线程安全? 如果他们没有共享,则线程安全 如果被共享: 只有读操作,则线程安全 有写操作,则这段代码是临界区,需要考虑线程安全 2.局部变量是否线程安全 局部变量是线程安全的 当局部变量引用的对象则未必 如果给i对象没有逃离方法的作用

    2024年02月08日
    浏览(52)
  • Java面试_并发编程_线程基础

    进程是正在运行程序的实例, 进程中包含了线程, 每个线程执行不同的任务 不同的进程使用不同的内存空间, 在当前进程下的所有线程可以共享内存空间 线程更轻量, 线程上下文切换成本一般上要比进程上下文切换低(上下文切换指的是从一个线程切换到另一个线程) 并发是单个

    2024年02月07日
    浏览(48)
  • 【Java 并发编程】Java 线程本地变量 ThreadLocal 详解

    先一起看一下 ThreadLocal 类的官方解释: 用大白话翻译过来,大体的意思是: ThreadLoal 提供给了 线程局部变量 。同一个 ThreadLocal 所包含的对象,在不同的 Thread 中有不同的副本。这里有几点需要注意: 因为每个 Thread 内有自己的实例副本,且 该副本只能由当前 Thread 使用 。

    2024年02月04日
    浏览(66)
  • 【Java并发编程】线程中断机制(辅以常见案例)

    本文由浅入深介绍了中断机制、中断的常见案例和使用场景。 因为一些原因需要取消原本正在执行的线程。我们举几个栗子: 假设踢足球点球时,A队前4轮中了4个球,B队前4轮只中了2个球,此时胜负已分,第5轮这个点球就不用踢了,此时需要停止A队的线程和B队的线程(共

    2024年02月13日
    浏览(34)
  • java并发编程:多线程基础知识介绍

    最初的计算机只能接受一些特定的指令,用户每输入一个指令,计算机就做出一个操作。当用户在思考或者输入时,计算机就在等待。这样效率非常低下,在很多时候,计算机都处在等待状态。 后来有了 批处理操作系统 ,把一系列需要操作的指令写下来,形成一个清单,一次

    2024年02月07日
    浏览(49)
  • Java并发编程学习16-线程池的使用(中)

    上篇分析了在使用任务执行框架时需要注意的各种情况,并简单介绍了如何正确调整线程池大小。 本篇将继续介绍对线程池进行配置与调优的一些方法,详细如下: ThreadPoolExecutor 为 Executors 中的 newCachedThreadPool 、 newFixedThreadPool 和 newScheduledThreadExecutor 等工厂方法返回的 Exe

    2024年02月10日
    浏览(42)
  • Java并发编程学习笔记(一)线程的入门与创建

    认识 程序由指令和数据组成,简单来说,进程可以视为程序的一个实例 大部分程序可以同时运行多个实例进程,例如记事本、画图、浏览器等 少部分程序只能同时运行一个实例进程,例如QQ音乐、网易云音乐等 一个进程可以分为多个线程,线程为最小调度单位,进程则是作

    2024年02月16日
    浏览(51)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包