首页 文章 精选 留言 我的

精选列表

搜索[文档解析],共10000篇文章
优秀的个人博客,低调大师

ReentrantLock源码解析

谈到多线程,就不避开锁(Lock),jdk中已经为我们提供了好几种锁的实现,已经足以满足我们大部分的需求了,今天我们就来看下最常用的ReentrantLock的实现。 其实最开始是想写一篇关于StampedLock的源码分析的,但发现写StampedLock前避不开ReentrantReadWriteLock,写ReentrantReadWriteLock又避不开ReentrantLock,他们仨是逐层递进的关系。ReentrantReadWriteLock解决了一些ReentrantLock无法解决的问题,StampedLock又弥补了ReentrantReadWriteLock的一些不足,三者有各自的设计和有缺点,这篇文章先和你一起看下ReentrantLock,之后我们会再一起去了解ReentrantReadWriteLock和StampedLock,相信有了ReentrantLock的基础后面的内容也会容易理解很多。 相对于jdk中很多其他的类来说,ReentrantLock提供的接口已经算是非常简单,事实上它只有一个构造参数boolean fair,用来指定是公平锁还是非公平锁,如果你指定的话默认是非公平锁。 public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); } 什么是公平?这里的的公平是指每个线程可以有公平的机会获取到这把锁,10个线程竞争这把锁,某个线程各有10%的机会获取到锁。听起来很理想主义,但大多数时候不建议使用公平锁,因为局部性的存在,每个线程对锁的真正需求度是不同的,有些线程就是需要很频繁的占有锁,有些偶尔占有就行。如果你单纯是为了公平而导致供需不平衡,可能有些线程会浪费锁的持有时间,而有些线程急需用锁但迟迟获取不到,导致线程饥饿,最终导致整个系统的性能不是最大化的。 最大化锁的使用率和代码性能就成了锁设计最重要的目标。试想如果我们提前知道每个线程对锁的需求度,然后按需求度给他们分配锁的占有机会,这样必然能达到锁的最优使用率。但实际上对于jdk的开发者来说,他哪知道你要拿锁去做啥、要开几个线程?所以ReentrantReadWriteLock的设计者用一种很简单粗暴的方式解决了大部分的问题,我们直接上源码。 ReentrantLock中最核心的就是Sync的实现,它默认已经实现了非公平锁的功能,所以你会看到NonfairSync只是简简单单继承了Sync而已。而Sync的主要功能还是继承了AbstractQueuedSynchronizer(AQS)。 AbstractQueuedSynchronizer 简单来说就是维护了一个状态 __state__,和一个等待线程队列(一个双向链表),然后通过VarHandle提供的CAS操作保证线程安全。当你在调用lock()的时候,会直接调用到AbstractQueuedSynchronizer中的acquire(int arg) public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); } acquire也很简单,尝试去获取锁,如果获取失败,就把当前线程加到等待队列里(排队),并把当前线程的状态设置为可中断。 回到ReentrantLock,我们来开下它是如何依赖Sync来实现非公平锁的。NonfairSync在执行tryAcquire(int arg)的时候,实际执行的是以下代码。 final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { //如果state是0,说明当前没有线程持有锁,用CAS更新状态,如果CAS成功,就在锁中写入当前线程的信息。 if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { //如果state不是0,也不一定获取锁失败,要看下持有锁的线程是不是自己,如果是更新state int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; } 从这可以看出来ReentrantLock是可重入锁,state的目的就是为了记录当前锁被同一个线程获取了几次。但是看完这段代码你肯定没看出来哪 __不公平了__。别急,我们来对比下公平锁的实现就知道了。 static final class FairSync extends Sync { private static final long serialVersionUID = -3000897897090466540L; @ReservedStackAccess protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (!hasQueuedPredecessors() && // 不同之处 compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; } } FairSync也是继承自Sync,但它重写了加锁tryAcquire方法,打眼一看和上面非公平锁的tryAcquire非常像,唯一不同之处就是在state为0时多了个!hasQueuedPredecessors()的判断。hasQueuedPredecessors()方法是判断是否有线程在等待去获取这把锁,如果有其他线程这次就算是获取锁失败了。 来个易懂的例子,现在办公室只有一间卫生间,很多个同事共用,有人在用卫生间的时候也不会希望别人跑进来和他一起用。公平锁的实现方式就是我来上卫生间,发现卫生间没人用,但有人在排队等卫生间(可能是玩手机没注意卫生间空了),我只能乖乖排队。非公平锁的实现方式是,我来上卫生间,发现卫生间是空的,不管有没有人排队我都占了,这样显然对其他排队的人来说是不公平的。 这种方式在现实世界看起来是非常不合理的,但是如果换种视角,可能越着急的人才是越需要用卫生间的人(可能他拉肚子),让排队的人多等会无所谓,这样才能最大化卫生间的的价值。虽然拉肚子在现实世界不常见,在在计算机中以纳秒计的世界里,有些线程就是比其他线程急很多的情况非常常见,非公平的方式就很合情。再从概率的角度看,如果有个线程需要以更高的频次使用这把锁,不排队去获取锁能舍得锁被获取到的次数最大化,也很合理。所以非公平锁合情合理。但历史告诉我们,凡事没有绝对,还是需要具体问题具体分析,有些情况下,非公平锁会导致线程饥饿。 public void unlock() { sync.release(1); } public final boolean release(int arg) { if (tryRelease(arg)) { Node h = head; if (h != null && h.waitStatus != 0) unparkSuccessor(h); return true; } return false; } unlock()调用了sync中的release(),release()是继承自AQS,跳到AQS中就会发现又调用了tryRelease()。ReentrantLock重写了tryRelease(),源码如下,也比较简单。 protected final boolean tryRelease(int releases) { int c = getState() - releases; if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; if (c == 0) { free = true; setExclusiveOwnerThread(null); } setState(c); return free; } 释放锁的过程是先判断是否是锁持有线程,然后更新锁状态。如果你进到setExclusiveOwnerThread(null)和setState(c)里面,就会发现这里没有用到CAS,会不会出现线程安全的问题?仔细想想其实不会有线程安全的问题, if (Thread.currentThread() != getExclusiveOwnerThread())判断了是当前线程是否持有锁,保证后续逻辑只有持有锁的线程才会执行到,因为之前获取锁是用CAS保证线程安全的,所以后面的逻辑也一定是线程安全的。 除了加锁和释放锁外,ReentrantLock还提供了和锁、线程相关的的接口,如上图,从函数名就可以看出其作用了,而且实现代码比较简单,这里就不再赘述了,有兴趣可以自行查看源码。

优秀的个人博客,低调大师

Jsp 无法解析${}

错误原因1:未导包 解决: maven依赖 <!-- JSTL for JSP --> <!-- https://mvnrepository.com/artifact/javax.servlet/jstl --> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/taglibs/standard --> <dependency> <groupId>taglibs</groupId> <artifactId>standard</artifactId> <version>1.1.2</version> </dependency> 手动导入 图1.png 错误原因2:配置 解决: 在首行设置isELIgnored为false,即不忽略EL表达式 <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" isELIgnored="false"%>

优秀的个人博客,低调大师

__cdecl __stdcall 解析

1.如果函数func是__cdecl(默认调用方式),调用时情况如下 int main() { //参数从右到左压栈 push4 push3 push2 push1 call func add esp0x10//调用者恢复堆栈指针esp,4个参数的大小是0x10(4x4) } 2.如果函数func是__stdcall,调用时情况如下 int main() { //参数从右到左压栈 push4 push3 push2 push1 call func //恢复堆栈指针由被调用者func负责,方法是"ret 0x10" } 3.如果函数func是__pascal,调用情况如下 int main() { //参数从左到右压栈 push1 push2 push3 push4 call func //恢复堆栈指针由被调用者func负责,方法是"ret 0x10" } 3.如果函数func是__fastcall,调用情况如下 int main() { //参数先用ecx, edx, eax传递,然后再压栈 //不进栈 //(不知为什么,帮助中写的是从左到右传递的, //是不是错了,还是bcb6和bcb5的不一样) push4 mov ecx3 mov edx2 mov eax1 call func //恢复堆栈指针由被调用者func负责,方法是"ret 0x04", //因为只进栈一个参数,其余用寄存器传递,所以用ret 0x04恢复 } 发表者:huang_jh #define callback __stdcall #define winapi __stdcall 定义成不同的名字只是为了"望文知意"就像hwnd和hcursor是一样的类型. 他们都是窗口函数(过程)...... 发表者:sinman 我收集的,全仍上来了 左通过栈传递,被调用的函数在返回前清理传送参数的内存栈,但不同的是函数名的修饰部分。 _stdcall是pascal程序的缺省调用方式,通常用于win32 api中,函数采用从右到左的压栈方式,自己在退出时清空堆栈。vc将函数编译后会在函数名前面加上下划线前缀,在函数名后加上"@"和参数的字节数。 2、c调用约定按从右至左的顺序压参数入栈,由调用者把参数弹出栈。对于传送参数的内存栈是由调用者来维护的。另外,在函数名修饰约定方面也有所不同。 _cdecl是c和c++程序的缺省调用方式。每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用_stdcall函数的大。函 数采用从右到左的压栈方式。vc将函数编译后会在函数名前面加上下划线前缀。是mfc缺省调用约定。 3、__fastcall调用约定是“人”如其名,它的主要特点就是快,因为它是通过寄存器来传送参数的或更小的参数,剩下的参数仍旧自右向左压栈传送, 被调用的函数在返回前清理传送参数的内存栈),在函数名修饰约定方面,它和前两者均不同。 _fastcall方式的函数采用寄存器传递参数,vc将函数编译后会在函数名前面加上"@"前缀,在函数名后加上"@"和参数的字节数。 4、thiscall仅仅应用于“c++”成员函数。this指针存放于cx寄存器,参数从右到左压。thiscall不是关键词,因此不能被程序员指定。 5、naked call采用1-4的调用约定时,如果必要的话,进入函数时编译器会产生代码来保存esi,edi,ebx,ebp寄存器,退出函数时则产生代码恢复这些 寄存器的内容。naked call不产生这样的代码。naked call不是类型修饰符,故必须和_declspec共同使用。 关键字 __stdcall、__cdecl和__fastcall可以直接加在要输出的函数前,也可以在编译环境的setting...\c/c++ \code generation项选择。当加在输出函数前的关键字与编译环境中的选择不同时,直接加在输出函数前的关键字有效。它们对应的命令行参数分别为/gz、 /gd和/gr。缺省状态为/gd,即__cdecl。 要完全模仿pascal调用约定首先必须使用__stdcall调用约定,至于函数名修饰约定,可以通过其它方法模仿。还有一个值得一提的是winapi 宏,windows.h支持该宏,它可以将出函数翻译成适当的调用约定,在win32中,它被定义为__stdcall。使用winapi宏可以创建自己 的apis。 2)名字修饰约定 1、修饰名(decoration name) “c”或者“c++”函数在内部通过修饰名识别。修饰名是编译器在编译函数定义或者原型时生成的字符串。有些情况下使用函数的修饰名是必要的,如在模块定 义文件里头指定输出“c++”重载函数、构造函数、析构函数,又如在汇编代码里调用“c””或“c++”函数等。 修饰名由函数名、类名、调用约定、返回类型、参数等共同决定。 2、名字修饰约定随调用约定和编译种类(c或c++)的不同而变化。函数名修饰约定随编译种类和调用约定的不同而不同,下面分别说明。 a、c编译时函数名修饰约定规则: __stdcall调用约定在输出函数名前加上一个下划线前缀,后面加上一个“@”符号和其参数的字节数,格式为_functionname@number。 __cdecl调用约定仅在输出函数名前加上一个下划线前缀,格式为_functionname。 __fastcall调用约定在输出函数名前加上一个“@”符号,后面也是一个“@”符号和其参数的字节数,格式为@functionname@number。 它们均不改变输出函数名中的字符大小写,这和pascal调用约定不同,pascal约定输出的函数名无任何修饰且全部大写。 b、c++编译时函数名修饰约定规则: __stdcall调用约定: 1、以“?”标识函数名的开始,后跟函数名; 2、函数名后面以“@@yg”标识参数表的开始,后跟参数表; 3、参数表以代号表示: x--void , d--char, e--unsigned char, f--short, h--int, i--unsigned int, j--long, k--unsigned long, m--float, n--double, _n--bool, .... pa--表示指针,后面的代号表明指针类型,如果相同类型的指针连续出现,以“0”代替,一个“0”代表一次重复; 4、参数表的第一项为该函数的返回值类型,其后依次为参数的数据类型,指针标识在其所指数据类型前; 5、参数表后以“@z”标识整个名字的结束,如果该函数无参数,则以“z”标识结束。 其格式为“?functionname@@yg*****@z”或“?functionname@@yg*xz”,例如 int test1-----“?test1@@yghpadk@z” void test2 -----“?test2@@ygxxz” __cdecl调用约定: 规则同上面的_stdcall调用约定,只是参数表的开始标识由上面的“@@yg”变为“@@ya”。 __fastcall调用约定: 规则同上面的_stdcall调用约定,只是参数表的开始标识由上面的“@@yg”变为“@@yi”。 vc++对函数的省缺声明是"__cedcl",将只能被c/c++调用. cb在输出函数声明时使用4种修饰符号 //__cdecl cb的默认值,它会在输出函数名前加_,并保留此函数名不变,参数按照从右到左的顺序依次传递给栈,也可以写成_cdecl和cdecl形式。 //__fastcall 她修饰的函数的参数将尽肯呢感地使用寄存器来处理,其函数名前加@,参数按照从左到右的顺序压栈; //__pascal 它说明的函数名使用pascal格式的命名约定。这时函数名全部大写。参数按照从左到右的顺序压栈; //__stdcall 使用标准约定的函数名。函数名不会改变。使用__stdcall修饰时。参数按照由右到左的顺序压栈,也可以是_stdcall; 发表者:echoher far是古代的东西 在16位模式下,指针是16位的 指针的寻址空间只有64k 如果指定far,说明这个指针指向的地址要加上基地址 就是说用far可以指定64k以外的区域 现在已经没用了 一点用也没有了 原文http://hi.baidu.com/_%E2d_%B7%B3_%DE%B2%C2%D2/blog/item/4e48666da7c769ff4316948c.html ============================================================================== 本文转自被遗忘的博客园博客,原文链接:http://www.cnblogs.com/rollenholt/articles/2423120.html,如需转载请自行联系原作者

优秀的个人博客,低调大师

android fastjson 解析

JSONObject jsonObject = JSON.parseObject(wsResponse);String recommends = jsonObject.getString("recommends");Log.i("ss","__________________________recommends:"+recommends); List<Recommend> contents = JSON.parseArray(recommends, Recommend.class); Log.i("ss","____________________________contents.size:"+contents.size());注意: 1:是Integer不是int2:是implements Serializable,不是implements Serializable, Parcelable public class Recommend implements Serializable { private Integer mediaId; private String title; private Integer timeSpan; private Integer roomId; private String roomName; private Integer gameId; private Float overallScore; private Integer scoreType; private Integer totalViews; private Integer definition; private String createTime; } 本文转自wanqi博客园博客,原文链接http://www.cnblogs.com/wanqieddy/p/4655032.html,如需转载请自行联系原作者

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册