【Java】LockSupport原理与使用

LockSupport:

关键字段

    private static final sun.misc.Unsafe UNSAFE;

    private static final long parkBlockerOffset;

        Unsafe:"魔法类",较为底层,在LockSupport类中用于线程调度(线程阻塞、线程恢复等)。

        parkBlockerOffset:锁对象的内存偏移量。

关键方法

        (1)park()

public static void park() {
        UNSAFE.park(false, 0L);
    }


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

        可以看到LockSupport的park方法实现实际是由Unsafe类完成的。在Unsafe类中,park方法被native修饰,表示方法不是由Java语言实现,而是由C、C++提供,native关键字的作用是告诉编译器方法的实现将在程序运行时通过本地调用实现。

        第一个参数为false,代表线程相对当前时间阻塞;第二个参数为0L,代表阻塞的时间长度(第一个参数为true时才起作用,表示阻塞多久)。这两个参数共同作用表示当前线程挂起,直至有线程调用unpark(Thread t)方法,或者当前阻塞线程被其它线程打断。

        (2)unpark()

public static void unpark(Thread thread) {
        if (thread != null)
            UNSAFE.unpark(thread);
    }

public native void unpark(Object var1);

        方法逻辑十分简单,对传入的Thread对象进行判空操作,随后通过Unsafe类解除挂起线程的阻塞状态。

        (3)park(Object blocker)

public static void park(Object blocker) {
        //获取当前线程
        Thread t = Thread.currentThread();
        //设置线程的锁对象
        setBlocker(t, blocker);
        //阻塞当前线程
        UNSAFE.park(false, 0L);
        //解除阻塞后,将锁对象置空
        setBlocker(t, null);
    }


//设置线程的锁对象
private static void setBlocker(Thread t, Object arg) {
        // Even though volatile, hotspot doesn't need a write barrier here.
        // t:当前线程
        // parkBlockerOffset:锁对象的内存偏移量
        // arg:锁对象
        UNSAFE.putObject(t, parkBlockerOffset, arg);
    }


//返回线程的锁对象
public static Object getBlocker(Thread t) {
        //参数为null,则抛出空指针异常
        if (t == null)
            throw new NullPointerException();
        //否则使用Unsafe类通过parkBlockerOffset内存偏移量找到锁对象并返回
        return UNSAFE.getObjectVolatile(t, parkBlockerOffset);
    }


        该方法的作用也是使当前线程阻塞,与无参的park相比多了一个锁对象:blocker,如果我们想让线程阻塞于某一对象,以便我们更好的了解和管理线程的阻塞状态就可以使用该方法。

        (4)parkUntil(long deadline)

public static void parkUntil(long deadline) {
        UNSAFE.park(true, deadline);
    }

       第一个参数为true代表绝对时间阻塞;第二个参数deadline由caller线程传递,代表截止时间。两个参数共同作用表示线程会一直阻塞,直至有线程调用unpark解除阻塞、当前阻塞线程被打断、到达deadline时间,三个条件有一个成立即可脱离阻塞状态。

使用

        LockSupport的使用与Semaphore有一定相似之处,不同点在于LockSupport的permit数量变更与Semaphore不同。

park方法

        每次调用park方法都会消耗一个permit。如果线程在调用park方法时已经存在一个permit,那么当前线程不会进入阻塞状态,而是消耗掉这个permit并立即返回;如果线程在调用park方法时没有permit(初始时,默认permit为0),那么当前线程会进入阻塞状态。图解如下:

unpark方法

        unpark方法与park()方法并不一一对应:当permit数量为0个时,线程调用unpark方法会使permit数量+1;当permit数量为1个时,再次调用unpark方法不会让permit继续累加,这意味着permit的最大值为1,最小值为0。图解如下:

        测试

package test;
import java.util.concurrent.locks.LockSupport;

public class LockSupportTest {
    public static void main(String[] args) {


        Thread t = new Thread(() -> {
            try {
                //这里是为了让main线程先执行unpark动作
                Thread.sleep(150);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            long start = System.currentTimeMillis();
            System.out.println("调用park方法,permit数量为1则消耗掉一个permit,线程不阻塞;permit数量为0则当前线程进入阻塞状态");
            LockSupport.park();
            long end = System.currentTimeMillis();
            System.out.println("经过:"+(end - start) +"ms后,脱离阻塞状态");
        },"Thread-1");
        t.start();

        
        System.out.println("main线程让permit数量从0变为1");
        LockSupport.unpark(t);
    }
}

        测试结果

        测试结果表明其它线程可以提前调用unpark方法,让permit数量变成1,当既定线程调用park方法时,由于permit数量不为0所以不会陷入阻塞,而是立即返回。

        让我们来看看permit能不能累加

package test;
import java.util.concurrent.locks.LockSupport;

public class LockSupportTest {
    public static void main(String[] args) {


        Thread t = new Thread(() -> {
            try {
                //这里是为了让main线程先执行unpark动作
                Thread.sleep(150);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("调用park方法,permit数量为1则消耗掉一个permit,线程不阻塞;permit数量为0则当前线程进入阻塞状态");
            LockSupport.park();
            System.out.println("调用park方法,permit数量为1则消耗掉一个permit,线程不阻塞;permit数量为0则当前线程进入阻塞状态");
            LockSupport.park();
            System.out.println("脱离阻塞状态");
        },"Thread-1");
        t.start();

        //main
        System.out.println("main线程调用两次unpark方法");
        LockSupport.unpark(t);
        LockSupport.unpark(t);
    }
}

        测试结果

        我们可以看到,即使main线程调用了两次unpark方法,但Thread-1线程还是阻塞于第二次park方法调用,这直接证明:permit不能累加,permit的最大值为1。

        LockSupport相较于传统wait/notify实现线程同步的特点是简洁,整个过程没有锁对象的参与,让我们通过三线程交替打印A、B、C来感受一下LockSupoort的魅力:

package test;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.LockSupport;


//三线程交替打印A、B、C,一共打印4次
public class LockSupportTest {

    static AtomicInteger i = new AtomicInteger(0);
    static Thread[] threads = new Thread[3];
    public static void main(String[] args) {

        threads[0] = new Thread(() -> {
            while (i.get() < 12){
                System.out.print("A-" + i.getAndIncrement() + "   ");
                LockSupport.unpark(threads[1]);
                LockSupport.park();
            }
        },"Thread-1");
        threads[1] = new Thread(() -> {
            LockSupport.park();
            while (i.get() < 12){
                System.out.print("B-" + i.getAndIncrement() + "   ");
                LockSupport.unpark(threads[2]);
                if (i.get() > 10)
                    break;
                LockSupport.park();
            }
        },"Thread-2");

        threads[2] = new Thread(() -> {
            LockSupport.park();
            while (i.get() < 12){
                System.out.println("C-" + i.getAndIncrement());
                LockSupport.unpark(threads[0]);
                if(i.get() > 11)
                    break;
                LockSupport.park();
            }
        },"Thread-3");

        threads[0].start();
        threads[1].start();
        threads[2].start();
    }
}

        运行结果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/294979.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Unity】如何在Unity中使用C#的NuGet 包资源

【背景】 Unity的脚本语言是C#&#xff0c;而C#有很多功能和能力可以通过nuget包提供。有没有办法把这些能力结合到Unity中一起使用呢&#xff1f;如果可以&#xff0c;那将大大扩展Unity中各类功能实现的便捷性。 【方法】 答案是&#xff1a;你可以&#xff01; 获取Nuge…

经典八股文之RocketMQ

核心概念 NameServer nameserver是整个rocketmq的大脑&#xff0c;是rocketmq的注册中心。broker在启动时向所有nameserver注册。生产者在发送消息之前先从 NameServer 获取 Broker 服务器地址列表(消费者一 样)&#xff0c;然后根据负载均衡算法从列表中选择一台服务器进行消…

SSM在线手机品牌商城----计算机毕业设计

项目介绍 该项目为前后台项目&#xff0c;分为普通用户与管理员两种角色&#xff0c;前台普通用户登录&#xff0c;后台管理员登录&#xff1b; 管理员角色包含以下功能&#xff1a; 管理员登录,用户管理,品牌管理,子品牌管理,商品管理,订单管理,留言板管理等功能。 用户角…

Note: A Journey Across Canada

A Journey Across Canada 一场横穿加拿大的旅行 across journey After a quiz last autumn, Kuang crossed the continent eastward to Toronto to visit his schoolmate, the distance measuring approximately 5000 kilometers. 去年秋天一次考试后&#xff0c;Kuang向东穿…

Ubuntu 安装 JMeter:为你的服务器配置做好准备

Apache JMeter 是一个开源的负载测试工具&#xff0c;可以用于测试静态和动态资源&#xff0c;确定服务器的性能和稳定性。在本文中&#xff0c;我们将讨论如何下载和安装 JMeter。 安装 Java&#xff08;已安装 Java 的此步骤可跳过&#xff09; 要下载 Java&#xff0c;请遵…

AI小冰入驻淘宝 将提供虚拟人陪伴服务

AI小冰正式入驻淘宝&#xff01; 据悉&#xff0c;小冰在淘宝开出了“小冰旗舰店”、以及手淘小程序“X Eva 克隆人的平行世界”&#xff0c;为消费者提供基于KOL虚拟人带来的陪伴服务体验。用户搜索“小冰旗舰店”就可以直达店铺进行选购。 ​小冰旗舰店的首批商品包括冰花直充…

【设计模式-5】抽象工厂模式的代码实现及使用场景

前面我们了解到工厂方法模式通过引入抽象工厂的概念&#xff0c;使得产品对象的创建可以依赖于具体工厂&#xff0c;但是这种设计模式最大的问题是会造成类的数量爆炸式增长。对于这个问题&#xff0c;抽象工厂模式通过引入两个新的概念&#xff1a;产品等级与产品簇&#xff0…

ant-design-vue 使用本地iconfont.js

createFromIconfontCN只能使用【在线资源】&#xff0c;但是在线资源存在不稳定的风险 有人提了issue&#xff0c;不过目前也没有解决&#xff0c;但是有人提出了一种新的的解决方案 参考链接&#xff1a; https://github.com/ant-design/ant-design/issues/16480 main.js im…

【UML建模】部署图(Deployment Diagram)

1.概述 部署图是一种结构图&#xff0c;用于描述软件系统在不同计算机硬件或设备上的部署和配置情况&#xff0c;以图形化的方式展示系统中组件、节点和连接之间的物理部署关系。 通过部署图&#xff0c;可以清晰地了解系统的物理结构和部署方式&#xff0c;包括系统组件和节…

prometheus grafana mysql监控配置使用

文章目录 前传bitnami/mysqld-exporter:0.15.1镜像出现了问题.my.cnf可以用这个"prom/mysqld-exporter:v0.15.0"镜像重要的事情mysql监控效果外传 前传 prometheus grafana的安装使用&#xff1a;https://nanxiang.blog.csdn.net/article/details/135384541 本文说…

软件测试|SQL AND和OR运算符解析

简介 在SQL&#xff08;Structured Query Language&#xff09;中&#xff0c;AND和OR是两个常用的逻辑运算符。它们用于组合条件来构建复杂的查询语句&#xff0c;帮助我们更精确地过滤和检索数据。本文将详细介绍SQL中的AND和OR运算符&#xff0c;包括其语法、用法以及使用时…

Matlab绘制动态心形线

1. 代码 for alpha0:0.1:30 x-1.8:0.001:1.8; y(x.^2).^(1/3)0.9*(3.3-x.^2).^(1/2).*sin(alpha*pi*x); plot(x,y,r-,LineWidth,1.2); set(gca,YGrid,on); axis([-3,3,-2,4]); text(-2,3.35,$f(x)x^{\frac{2}{3}}0.9(3.3-x^2)^{\frac{1}{2}}sin(\alpha\pi x)$,Interpreter,lat…

Python Gui图形化开发

PyQt5、Tkinter、Kivy等GUI工具&#xff0c;助你轻松构建Python应用。新手友好的PySimpleGUI&#xff0c;高交互性的PyForms&#xff0c;助你搭建理想用户界面。 学习编程&#xff0c;不仅要学习MySQL以及编程语言和并行架构之间的关系这类基础知识&#xff0c;还有一个重要的…

openGauss 5.0.0企业版一主一备安装部署

目录 一、环境准备 1. 华为云购买两台ECS 1.1查看openEuler版本&#xff0c;操作系统版本及CPU的制式是基础 1.2查看CPU模式 1.3操作系统环境准备 2. 集群配置XML文件准备&#xff1a; 2.1集群参数配置&#xff1a; 2.2主机参数配置&#xff1a; 2.3备机参数配置&…

静态网页设计——校园官网(HTML+CSS+JavaScript)

前言 声明&#xff1a;该文章只是做技术分享&#xff0c;若侵权请联系我删除。&#xff01;&#xff01; 使用技术&#xff1a;HTMLCSSJS 主要内容&#xff1a;对学校官网的结构进行模仿&#xff0c;对布局进行模仿。 主要内容 1、首页 首页以多个div对页面进行分割和布局…

Fiddler抓包工具之fiddler界面工具栏介绍

Fiddler界面工具栏介绍 &#xff08;1&#xff09;WinConfig&#xff1a;windows 使用了一种叫做“AppContainer”的隔离技术&#xff0c;使得一些流量无法正常捕获&#xff0c;在 fiddler中点击 WinConfig 按钮可以解除这个诅咒&#xff0c;这个与菜单栏 Tools→Win8 Loopback…

CSS 缩小旋转动画

<template><div class="container" @mouseenter="startAnimation" @mouseleave="stopAnimation"><!-- 旋方块 --><div class="box" :class="{ rotate-scale-down: isAnimating }"><!-- 元素内容…

bat批处理文件_输出内容到文本

文章目录 1、echo str > test.txt&#xff08;覆盖原有内容&#xff09;2、echo str >> test.txt&#xff08;不覆盖原有内容&#xff0c;追加&#xff09; 1、echo str > test.txt&#xff08;覆盖原有内容&#xff09; 2、echo str >> test.txt&#xff0…

JavaWeb基础(3)-会话技术(cookie和session),过滤器(Filter),监听器(Listener)

JavaWeb基础&#xff08;3&#xff09;-会话技术(cookie和session)&#xff0c;过滤器(Filter)&#xff0c;监听器(Listener) 文章目录 JavaWeb基础&#xff08;3&#xff09;-会话技术(cookie和session)&#xff0c;过滤器(Filter)&#xff0c;监听器(Listener)8 会话技术(Coo…

【28】Kotlin语法进阶——使用协程编写高效的并发程序

提示&#xff1a;此文章仅作为本人记录日常学习使用&#xff0c;若有存在错误或者不严谨得地方欢迎指正。 文章目录 一、Kotlin中的协程1.1 协程的基本用法1.1.1协程与协程作用域1.1.2 使用launch函数创建子协程1.1.3 通过suspend关键声明挂起函数1.1.4 coroutineScope函数 1.2…