Java LockSupport学习

news2024/11/17 6:22:52
面试题:
1、LockSupport为什么可以先唤醒线程后阻塞线程?
因为unpark()获得了一个凭证,之后再调用park()方法,就可以名正言顺的消费凭证,故不会阻塞。
2、LockSupport为什么唤醒两次后阻塞两次,但最终结果还会阻塞线程?
因为凭证的数量最多为1,连续调用两次unpark()和调用一次unpark效果一样,只会增加一个凭证,
而调用两次park()却需要消费两个凭证,证不够,不能放行。

目录

一、概述

1、wait() && notify()

异常1:wait() && notify(),两个方法都去掉同步代码块执行

异常2:将notify放在wait方法前执行,程序无法唤醒,无法执行。

2、Condition中的await()、signal()实现线程等待唤醒

异常1:await() && signal(),两个方法都去掉锁执行

异常2:将signal()放在await方法前执行,程序无法唤醒,无法执行。

3、LockSupport类中的park()阻塞和unpark()唤醒

3.1、park() && park​(Object blocker):阻塞当前线程/阻塞传入的具体线程

3.2、unpark​(Thread thread):唤醒处于阻塞状态的指定线程

3.3、park() && unpark​(Thread thread)阻塞唤醒

3.4、先unpark​(Thread thread)后park()方法:先唤醒后阻塞,依旧正常执行

3.5、LockSupport总结


一、概述

java.util.concurrent.locks下的类LockSupport:用于创建锁和其他同步类的基本线程阻塞原语。

public class LockSupportextends Object

park():阻塞线程

unpark():解除阻塞线程 

LockSupport:是线程等待唤醒机制(wait/notify)的加强版

 让线程等待和唤醒的方法:

方式1:使用Object中的wait()方法让线程等待,使用Object中的notify()方法唤醒线程

方式2:使用JUC包中Condition的await()方法让线程等待,使用signal()方法唤醒线程

方式3:LockSupport类可以阻塞当前线程以及唤醒指定被阻塞的线程

1、wait() && notify()

public class LockSupportDemo {

	static Object obj = new Object();

	public static void main(String[] args) {
		new Thread(() -> {
			synchronized (obj) {
				System.out.println(Thread.currentThread().getName() + "\t ---come in");
				try {
					obj.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName() + "\t ---被唤醒");
			}
		}, "A").start();

		new Thread(() -> {
			synchronized (obj) {
				obj.notify();
				System.out.println(Thread.currentThread().getName() + "\t ---唤醒");
			}
		}, "B").start();
	}

}

正常结果:

A	 ---come in
B	 ---唤醒
A	 ---被唤醒

异常1:wait() && notify(),两个方法都去掉同步代码块执行

public class LockSupportDemo {

	static Object obj = new Object();

	public static void main(String[] args) {
		new Thread(() -> {
			// synchronized (obj) {
			System.out.println(Thread.currentThread().getName() + "\t ---come in");
			try {
				obj.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + "\t ---被唤醒");
			// }
		}, "A").start();

		new Thread(() -> {
			// synchronized (obj) {
			obj.notify();
			System.out.println(Thread.currentThread().getName() + "\t ---唤醒");
			// }
		}, "B").start();
	}

}

异常结果:

A	 ---come in
Exception in thread "A" Exception in thread "B" java.lang.IllegalMonitorStateException
	at java.lang.Object.wait(Native Method)
	at java.lang.Object.wait(Object.java:502)
	at com.lwz.test.LockSupportDemo.lambda$0(LockSupportDemo.java:12)
	at java.lang.Thread.run(Thread.java:748)
java.lang.IllegalMonitorStateException
	at java.lang.Object.notify(Native Method)
	at com.lwz.test.LockSupportDemo.lambda$1(LockSupportDemo.java:22)
	at java.lang.Thread.run(Thread.java:748)

结论:Object类中的wait、notify、notifyAll用于线程等待唤醒的方法,都必须在synchronized内部执行(必须用到关键字synchronized)。

异常2:将notify放在wait方法前执行,程序无法唤醒,无法执行。

import java.util.concurrent.TimeUnit;

public class LockSupportDemo {

	static Object obj = new Object();

	public static void main(String[] args) {
		new Thread(() -> {
            // 暂停3秒线程A
			try {
				TimeUnit.SECONDS.sleep(3);
			} catch (InterruptedException e1) {
				e1.printStackTrace();
			}
			synchronized (obj) {
				System.out.println(Thread.currentThread().getName() + "\t ---come in");
				try {
					obj.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName() + "\t ---被唤醒");
			}
		}, "A").start();

		new Thread(() -> {
			synchronized (obj) {
				obj.notify();
				System.out.println(Thread.currentThread().getName() + "\t ---唤醒");
			}
		}, "B").start();
	}

}

结果:

B	 ---唤醒
A	 ---come in

结论:先wait后notify、notifyAll方法,等待中的线程才会被唤醒,否则无法唤醒

wait和notify方法必须要在同步代码块或者方法里面且成对出现使用,先wait后notify才行。

2、Condition中的await()、signal()实现线程等待唤醒

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockSupportDemo {

	static Lock lock = new ReentrantLock();
	static Condition condition = lock.newCondition();

	public static void main(String[] args) {
		new Thread(() -> {
			lock.lock();
			try {
				System.out.println(Thread.currentThread().getName() + "\t --come in");
				try {
					condition.await();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName() + "\t ---被唤醒");
			} finally {
				lock.unlock();
			}

		}, "A").start();

		new Thread(() -> {
			lock.lock();
			try {
				condition.signal();
				System.out.println(Thread.currentThread().getName() + "\t --唤醒");
			} finally {
				lock.unlock();
			}
		}, "B").start();
	}

}

正常结果:

A	 --come in
B	 --唤醒
A	 ---被唤醒

异常1:await() && signal(),两个方法都去掉锁执行

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockSupportDemo {

	static Lock lock = new ReentrantLock();
	static Condition condition = lock.newCondition();

	public static void main(String[] args) {
		new Thread(() -> {
//			lock.lock();
			try {
				System.out.println(Thread.currentThread().getName() + "\t --come in");
				try {
					condition.await();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName() + "\t ---被唤醒");
			} finally {
//				lock.unlock();
			}

		}, "A").start();

		new Thread(() -> {
//			lock.lock();
			try {
				condition.signal();
				System.out.println(Thread.currentThread().getName() + "\t --唤醒");
			} finally {
//				lock.unlock();
			}
		}, "B").start();
	}

}

异常结果:

A	 --come in
Exception in thread "A" Exception in thread "B" java.lang.IllegalMonitorStateException
	at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:151)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1261)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.fullyRelease(AbstractQueuedSynchronizer.java:1723)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2036)
	at com.lwz.test.LockSupportDemo.lambda$0(LockSupportDemo.java:18)
	at java.lang.Thread.run(Thread.java:748)
java.lang.IllegalMonitorStateException
	at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.signal(AbstractQueuedSynchronizer.java:1939)
	at com.lwz.test.LockSupportDemo.lambda$1(LockSupportDemo.java:32)
	at java.lang.Thread.run(Thread.java:748)

异常2:将signal()放在await方法前执行,程序无法唤醒,无法执行。

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockSupportDemo {

	static Lock lock = new ReentrantLock();
	static Condition condition = lock.newCondition();

	public static void main(String[] args) {
		new Thread(() -> {
			// 暂停3秒线程A
			try {
				TimeUnit.SECONDS.sleep(3);
			} catch (InterruptedException e1) {
				e1.printStackTrace();
			}
			lock.lock();
			try {
				System.out.println(Thread.currentThread().getName() + "\t --come in");
				try {
					condition.await();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName() + "\t ---被唤醒");
			} finally {
				lock.unlock();
			}

		}, "A").start();

		new Thread(() -> {
			lock.lock();
			try {
				condition.signal();
				System.out.println(Thread.currentThread().getName() + "\t --唤醒");
			} finally {
				lock.unlock();
			}
		}, "B").start();
	}

}

结果:

B	 --唤醒
A	 --come in

结论:

现象和wait、notify效果是一样的,线程先要获得并持有锁,必须在锁块(synchronized或lock)中

必须要先等待后唤醒,线程才能够被唤醒。

3、LockSupport类中的park()阻塞和unpark()唤醒

LockSupport类通过park()和unpark()方法来实现阻塞和唤醒线程的操作。

static void	park():除非许可证可用,否则禁用当前线程以进行线程调度。
static void	park​(Object blocker):除非许可证可用,否则禁用当前线程以进行线程调度。
static void	unpark​(Thread thread):如果给定线程尚不可用,则为其提供许可。
public class LockSupport
extends Object

Basic thread blocking primitives for creating locks and other synchronization classes. 

This class associates, with each thread that uses it, a permit (in the sense of the 
Semaphore class). A call to park will return immediately if the permit is available, 
consuming it in the process; otherwise it may block. A call to unpark makes the permit 
available, if it was not already available. (Unlike with Semaphores though, permits do 
not accumulate. There is at most one.) 

LockSupport:用于创建锁和其他同步类的基本线程阻塞原语。
LockSupport类使用了一种名为Permit(许可)的概念来做阻塞和唤醒功能,每个线程都有一个许可(Permit),
permit只有两个值1和零,默认是零。
可以把许可看成是一种(0,1)信号量(Semaphore),但与Semaphore不同的是,许可的累加上限是1.

3.1、park() && park​(Object blocker):阻塞当前线程/阻塞传入的具体线程

调用LockSupport.park();

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

permit默认是0,所以一开始调用park()方法,当前线程就会阻塞,直到别的线程将当前线程的
permit设置为1时,park()方法会被唤醒,然后会将permit再次设置为0并返回。

3.2、unpark​(Thread thread):唤醒处于阻塞状态的指定线程

调用LockSupport.unpark(thread);

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

调用unpark(thread)方法后,就会将thread线程的许可permit设置成1(注意多次调用unpark方法,不
会累加,permit值还是1)会自动唤醒thread线程,即之前阻塞中的LockSupport.park();方法会立即返回。

3.3、park() && unpark​(Thread thread)阻塞唤醒

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;

public class LockSupportDemo {

	public static void main(String[] args) {
		Thread t1 = new Thread(() -> {
			System.out.println(Thread.currentThread().getName() + "\t --come in");
			LockSupport.park();// 被阻塞..等待许可证
			System.out.println(Thread.currentThread().getName() + "\t ---被唤醒");
		}, "t1");
		t1.start();

		try {
			TimeUnit.SECONDS.sleep(3);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		Thread t2 = new Thread(() -> {
			LockSupport.unpark(t1);
			System.out.println(Thread.currentThread().getName() + "\t --唤醒");
		}, "t2");
		t2.start();
	}

}

结果:

t1	 --come in
t2	 --唤醒
t1	 ---被唤醒

3.4、先unpark​(Thread thread)后park()方法:先唤醒后阻塞,依旧正常执行

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;

public class LockSupportDemo {

	public static void main(String[] args) {
		Thread t1 = new Thread(() -> {
			try {
				TimeUnit.SECONDS.sleep(3);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + "\t --come in");
			LockSupport.park();// 被阻塞..等待许可证
			System.out.println(Thread.currentThread().getName() + "\t ---被唤醒");
		}, "t1");
		t1.start();

		Thread t2 = new Thread(() -> {
			LockSupport.unpark(t1);
			System.out.println(Thread.currentThread().getName() + "\t --唤醒");
		}, "t2");
		t2.start();
	}

}

结果:

t2	 --唤醒
t1	 --come in
t1	 ---被唤醒

3.5、LockSupport总结

LockSupport是用来创建锁和其他同步类的基本线程阻塞原语。

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

LockSupport提供park()和unpark()方法实现阻塞线程和接触线程阻塞的过程

LockSupport和每个使用它的线程都有一个许可证(permit)关联。permit相当于1,0的开关,默认是0,调用依次unpark()就加1变成1,调用一次park()会消费permit,也就是将1变为0,同时park()立即返回。如再次调用park()会变成阻塞(因为permit为零了会阻塞在这里,一直到permit变为1),这时调用unpark()会把permit置为1.

每个线程都有一个相关的permit,permit最多只有一个,重复调用unpark()也不会积累凭证。

形象的理解

线程阻塞需要消耗凭证(permit),这个凭证最多只有1个。

当调用park()方法时:

        如果有凭证,则会直接消耗掉这个凭证然后正常退出。

        如果无凭证,就必须阻塞等待凭证可用。

而unpark()则相反,它会增加一个凭证,但凭证最多只能有1个,累加无效。

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;

public class LockSupportDemo {

	public static void main(String[] args) {
		Thread t1 = new Thread(() -> {
			System.out.println(Thread.currentThread().getName() + "\t --come in");
			LockSupport.park();// 被阻塞..等待许可证
			LockSupport.park();// 被阻塞..等待许可证
			System.out.println(Thread.currentThread().getName() + "\t ---被唤醒");
		}, "t1");

		t1.start();

		try {
			TimeUnit.SECONDS.sleep(3);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		Thread t2 = new Thread(() -> {
			LockSupport.unpark(t1);
			LockSupport.unpark(t1);
			System.out.println(Thread.currentThread().getName() + "\t --唤醒");
		}, "t2");

		t2.start();
	}

}

结果:

t1	 --come in
t2	 --唤醒

结论:说明了unpark()只能发放一张许可证对应一个park()方法通行。

Java volatile学习

干我们这行,啥时候懈怠,就意味着长进的停止,长进的停止就意味着被淘汰,只能往前冲,直到凤凰涅槃的一天!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Android 实现沉浸式全屏

前言 本文总结 Android 实现沉浸式全屏的实现方式。 实现沉浸式全屏 在一些需要全屏显示的场景下,比如玩游戏、看横屏视频的时候,内容全屏,占满窗口的体验会让用户更加沉浸到对内容的消费中,带来好的用户体验。 沉浸式显示具体来说就是如状态栏和导航栏部分的显示效果调…

C#:Krypton控件使用方法详解(第五讲) ——kryptonPanel

今天介绍的Krypton控件中的kryptonPanel,下面开始介绍这个控件的属性:首先要介绍的是这个控件的外观属性:Cursor属性:表示鼠标移动过这个控件的时候,鼠标的显示状态。具体属性值有哪些,如下图所示&#xff…

第一批因ChatGPT坐牢的人,已经上路了

大家好,我是 Jack。 ChatGPT 的火爆有目共睹,有人靠着它赚了第一桶金,也有人靠着它即将吃上第一顿牢饭。 任何一件东西的火爆,总会给一些聪明人带来机会。 艾尔登法环火的时候,一堆淘宝卖魂的;羊了个羊火…

动漫人物眼睛画法

本期的动漫绘画课程教大家来学习动漫人物眼睛画法,结合板绘软件从草稿开始一步步教你画出动漫人物眼睛,不用报动漫培训班也能学会,快来跟着本期的动漫人物眼睛画法教程试试吧! 动漫人物眼睛画法步骤教程: 注意&#x…

Linux内核实现完全公平调度算法

Linux 进程调度算法经历了以下几个版本的发展: 基于时间片轮询调度算法。(2.6之前的版本)O(1) 调度算法。(2.6.23之前的版本)完全公平调度算法。(2.6.23以及之后的版本) 完全公平调度算法基本原理 完全公平调度算法 体现在对待每个进程都是公平的,那么…

ChatGPT AI 人工智能 开发路径

ChatGPT(全名:Chat Generative Pre-trained Transformer),美国OpenAI研发的聊天机器人程序,于2022年11月30日发布。 推荐以下几个AI 开发学习资源 一、GPTZero AI: GPTZero GPTZero 是普林斯顿大学学生 Edward Tian …

Java爬虫—WebMagic

一,WebMagic介绍WebMagic企业开发,比HttpClient和JSoup更方便一),WebMagic架构介绍WebMagic有DownLoad,PageProcessor,Schedule,Pipeline四大组件,并有Spider将他们组织起来&#xf…

MySQL中JSON数据类型详解

目录 概要及优点 JSON定义 JSON字段的增删改查操作 插入操作 查询操作 修改操作 删除操作 如何对JSON字段创建索引? 加索引查询结果分析: 不加索引查询结果分析: 使用JSON时的注意事项 概要及优点 JSON数据类型是MySQL5.7.8开始支持的…

FlowChartX/Diagramming for ActiveX 4.9.8 Crack

构建完美的图表 如果您的应用程序以 ActiveX 平台为目标,并且您需要实现图表功能,那么您所需要的只是 FlowChartX。它提供了创建、自定义和呈现流程图的所有功能。 ActiveX 图表库:分类图表 图 Diagramming for ActiveX该组件为您提供了一组…

浅谈C++函数重载

C相较于C语言来说,重载是一重大特性,让我们一起简单的回顾一下重载那些事 传送门函数重载是什么为什么有函数重载函数重载是如何实现的总结函数重载是什么 函数重载:是函数的一种特殊情况,C允许在同一作用域中声明几个功能相似的同名函数 这些同名函数的形参列表(参数个数or类…

day19_抽象类丶接口

由来 当我们声明一个几何图形类:圆、矩形、三角形类等,发现这些类都有共同特征:求面积、求周长、获取图形详细信息。那么这些共同特征应该抽取到一个公共父类中。但是这些方法在父类中又无法给出具体的实现,而是应该交给子类各自…

当遇到国外客户的问题,你解决不了的时候怎么办

对我来说,今年的这个春节假期有点长,差不多休了一个月。复工之后,截止目前做到了60万RMB的业绩,但是相较于往年,整体状态还是差了些。往年的春节,我都是随时待命的状态,整个春节天天坐于电脑前&…

JSP 和 JSTL

文章目录🍓摘要🍓一、JSP🍉1.1 JSP的基础语法🍫1.1.1 简介🍫1.1.2 依赖🍫1.1.3 注释🍫1.1.4 Scriptlet 脚本🍉1.2 JSP的指令标签🍫1.2.1 include 静态包含🍫1…

2023年数学建模美赛A题(A drought stricken plant communities)分析与编程

2023年数学建模美赛A题(A drought stricken plant communities)分析与编程 2023年数学建模美赛D题(Prioritizing the UN Sustainability Goals)分析与编程 特别提示: 1 本文介绍2023年美赛题目,进行深入分析…

台式计算机加固态硬盘,台式机添加固态硬盘教程_台式主机固态硬盘怎么安装-win7之家...

固态硬盘是用固态电子存储芯片阵列制成的硬盘,也是电脑中比较常见的内存硬件,有些用户在使用电脑时候,由于内存不足导致系统运行较卡的情况,往往会选择添加固态硬盘来解决,那么台式主机固态硬盘怎么安装呢?…

Ansible中的角色使用(ansible roles)

文章目录一、ansible 角色简介二、roles目录结构三、role存放的路径:配置文件ansible.cfg中定义四、创建目录结构五、playbook中使用rolesplaybook变量会覆盖roles中的定义变量六、控制任务执行顺序七、ansible—galaxy命令工具八、安装选择的角色1.从网上下载&…

2023.2.17-博客记录

1.斐波那契数列的复杂度 参考于:斐波那契数列时间复杂度 运用到了递归思想 那么,推导:时间复杂度 f(n) f(n-1) f(n-2) 每一层都包含一个加法操作 例如n 8时,T(n) 2^0 2^1 2^2 2^3 2^4 2^5 2^6 2^7-1 O(n) 2^7-1 2…

四六级真题长难句分析与应用

一、基本结构的长难句 基本结构的长难句主要考点:断开和简化 什么是长难句? 其实就是多件事连在了一块,这时候句子就变长、变难了 分析步骤: 第一件事就是要把长难句给断开,把多件事断开成一件一件的事情&#xff0…

ElementUI`resetFields()`方法避坑

使用ElementUI中的resetFields()方法有哪些注意点 场景一 场景一:当编辑弹出框和新增弹出框共用时,编辑数据后关闭编辑弹出框时调用this.$refs.form.resetFields()无法清空弹出框 问题代码: // 点击新增按钮handleAdd() {this.dialogVi…

《Qt6开发及实例》6-3 双缓冲机制

目录 一、原理与设计 1.1 原理 1.2 设计 二、绘图区的实现 2.1 鼠标移动事件 2.2 重绘函数&调整大小函数&清除屏幕 三、主窗口的实现 3.1 代码 一、原理与设计 1.1 原理 双缓冲就是在绘制控件时,将内容绘制在一个图片中,再将图片一次性…