RAII技术:在Rust中实现带有守卫的自旋锁,支持一定程度上的编译期并发安全检查

摘要

本文介绍了一种使用了RAII技术的自旋锁,配合Rust的生命周期及所有权机制,能够在减少代码量的同时,很好的解决自旋锁的“忘记放锁”、“双重释放”、“未加锁就访问”的并发安全问题。并且这种自旋锁能够支持编译期的检查,任何不符合以上安全要求的代码,将无法通过编译。

前言

对于许多编程语言默认提供的锁,加锁、放锁需要手动进行。手动加锁可以理解(这不废话嘛),但是,手动放锁的时机,总是难以控制。比如:在临界区内,执行过程中,如果程序出错了,在异常处理的过程中,忘记放锁,那么就会造成其他进程无法获得这个锁。传统的做法就是,人工寻找所有可能的异常处理路径,添加放锁的代码。这样做的话,能解决问题,但非常的繁琐,尤其是有多个锁的时候,更加如此。

并且,对于传统的语言,还可能存在锁的“双重释放”的问题,也就是:一个锁被进程A释放后,进程B对其加锁,接着,进程A的错误代码,执行了放锁操作,导致进程B的锁被过早地释放。这样的问题,当我们发现的时候,可能已经不是第一现场了,debug很困难。

并且,对于大部分的语言,锁与它所要保护的数据,并没有一种机制,告诉编译器/解释器:“这个锁,保护的就是这个数据对象”。因此,编译器很难检查出“未加锁就访问”的bug,程序员会经常犯这种错误(尤其是对于新手程序员,很难处理好锁的问题)。这样的代码,编译器无法保证其并发安全。

对于Rust,借助其生命周期、所有权机制,我们能够与RAII技术进行结合,能实现一种新的自旋锁,从而轻松解决以上的问题。

DragonOS中,实现了具有守卫的自旋锁,能够解决以上的问题,让新手程序员也能很容易的管理自旋锁。这样写出来的代码只要能够通过编译器的检查(就是能够编译通过),那么就不用担心以上提到的并发安全问题。本文将基于DragonOS中实现的自旋锁进行讲解。

具体的代码链接:http://opengrok.ringotek.cn/xref/DragonOS/kernel/src/libs/spinlock.rs?r=ec53d23e#137

什么是RAII技术?

RAII,全称资源获取即初始化(英语:Resource Acquisition IInitialization),它是在一些面向对象语言中的一种习惯用法。RAII源于C++,在许多的编程语言中都有应用。

RAII要求,资源的有效期与持有资源的对象的生命周期严格绑定,即由对象的构造函数完成资源的分配(获取),同时由析构函数完成资源的释放。在这种要求下,只要对象能正确地析构,就不会出现资源泄漏问题。

思路

由于Rust在语言层面就实现了生命周期与所有权机制,因此,能够很好的实现RAII,并且能够支持编译期检查,不符合安全要求的代码,将无法通过编译。我们的思路是:把要保护的数据的所有权,交给对应的锁来管理,不再需要程序员来手动管理“锁——被锁保护的数据”的关系。也就是说,这个自旋锁,拥有要保护的数据的所有权,其他的地方需要访问被保护的数据,都需要从自旋锁申请借用这个变量,获得可变引用/不可变引用。这个访问的权限,不是直接给到要用到数据的函数内的局部变量的,而是由一个叫做“守卫”的对象负责持有权限。访问数据时,都要经过这个守卫(请注意,得益于Rust的“零成本抽象”,这是没有运行时开销的)。当守卫变量的生命周期结束,其析构函数就执行“放锁”的动作。

自旋锁出借自己保护的数据的访问权限时,会执行加锁的动作,然后返回一个守卫。请注意,守卫只会在“自旋锁加锁成功”后被初始化。因此,对于一个自旋锁,最多存在1个守卫。并且,只要守卫的生命周期没有结束,我们都能通过这个守卫,来访问被保护的数据。

那么,我们来小结一下,基于RAII+所有权+生命周期机制的自旋锁,解决以上问题的途径:

  • 忘记放锁/出现异常退出时,未放锁:一旦守卫的生命周期结束,就会在析构函数中进行放锁。
  • “双重释放“问题:所有放锁操作只能由守卫对象的析构函数进行。由于守卫对象最多同时刻只有1个,并且,由于守卫对象只要生命周期没有结束,那么锁一定是被获取到的。因此避免了“双重释放”的问题。
  • “未加锁就访问被保护的数据“的问题:由于被保护的数据,其所有权属于自旋锁,并且是一个私有的字段。进程只能通过守卫来访问被保护的数据。而要获得守卫的方式只有1种:成功加锁。因此,它能解决“未加锁就访问”的问题。任何想要“不加锁就访问”的代码,都无法通过编译器的检查。

实现

上面说了思路,那么我们接下来就结合具体的代码,来介绍一下它的实现:

结构体定义

下图是SpinLock及其守卫的定义

对于SpinLock,其内部包含两个私有的成员变量:

  • lock:这是一个RawSpinlock,具体功能与其他语言的自旋锁一致,需要手动加锁、放锁,具有自旋锁的最基本功能。不具备编译期的并发安全检查的特性。
  • data:这个字段是自旋锁保护的数据。在自旋锁被初始化时,要被保护的数据,会被放到这个UnsafeCell中。请注意,UnsafeCell支持内部可变性,也就是说,被保护的数据的值可以被修改。

对于SpinLockGuard这个守卫,它只有1个成员变量,也就是SpinLock的不可变引用。并且,SpinLockGuard没有构造器,它只能通过SpinLock的lock()方法,在加锁后产生。

SpinLock实现

SpinLock只具有两个成员方法:new()和lock()。如下图所示:

  • new()方法:初始化lock字段,并且将数据放入data字段。请注意,由于传入的value不是引用,因此,value的所有权,在new()函数结束后,被移动到了data字段中。程序的其他部分,不再拥有这个value的所有权。在外部的其他函数中,任何尝试访问value的行为,都会被编译器阻止
  • lock()方法:本方法先对自旋锁进行加锁,然后返回一个守卫。请注意,lock()函数是唯一的获得守卫的途径。

同时,我们为SpinLock实现Sync这个Trait,这样,编译器就知道,SpinLock是线程安全的,它能在几个线程之间共享。(当然,我们要求T是实现了Send Trait的,因为只有这样,才意味着它能从一个进程发送给另一个进程)

SpinLockGuard实现

SpinLockGuard的实现也很简单,我们为它实现了3个trait: Deref、DerefMut、Drop。

  • Deref:当我们访问SpinLockGuard时,相当于访问被自旋锁保护的变量(不可变引用)
  • DerefMut:当我们访问SpinLockGuard时,相当于访问被自旋锁保护的变量(可变引用)
  • Drop:当SpinLockGuard的生命周期结束时,将会自动释放锁。

如何使用这样的自旋锁?

与传统的SpinLock需要反复确认变量在锁的保护之下相比,SpinLock的使用非常简单,只需要这样做:

在上面这个例子中,我们声明了一个SpinLock,并且把要保护的数据:一个Vec数组,传了进去。然后,我们在第3行,获取了锁。在接下来的几行中,我们通过这个守卫,来向Vec内部插入数据。当离开内部的闭包(由“{}”包裹)之后,在最后一行,我们通过打印,能发现,锁被自动的释放了。

对于结构体内部的变量,我们可以使用SpinLock进行细粒度的加锁,也就是使用SpinLock包裹需要细致加锁的成员变量,比如这样:

pub struct a {
  pub data: SpinLock<data_struct>,
}

那么,对data_struct类型的data字段的访问,必须先加锁,否则是无法访问它的。

当然,我们也可以对整个结构体进行加锁

struct MyStruct {
  pub data: data_struct,
}
/// 被全局加锁的结构体
pub struct LockedMyStruct(SpinLock<MyStruct>);

总结

本文介绍的自旋锁,使用了RAII技术,结合Rust的生命周期及所有权机制。将锁与被其保护的数据进行了绑定,使其能够支持编译期检查。减少了BUG的产生,也减轻了程序员手动维护“锁——被锁保护的数据”关系的负担。

附录

  • DragonOS中,SpinLock的实现:http://opengrok.ringotek.cn/xref/DragonOS/kernel/src/libs/spinlock.rs?r=ec53d23e#137
  • DragonOS代码仓库:https://github.com/fslongjin/DragonOS

转载请注明来源:https://longjin666.cn/?p=1678

本文转载于网络 如有侵权请联系删除

相关文章

  • 【vue3入门到入土】-- 响应式api用法及应用场景

    ref创建一个响应式数据,一般来说用于创建简单类型的响应式对象,比如String、Number类型constname=ref('tom') 复制代码复制可以看到,ref方法将这个字符串进行了一层包裹,返回的是一个RefImpl类型的对象,译为引用的实现(referenceimplement),在该对象上设置了一个不可枚举的属性value,所以使用name.value来读取值。正如上面所说,ref通常用于定义一个简单类型,那么是否可以定义一个对象或者数组?constobj=ref({ age:12, sex:'man', }) 复制代码复制控制台可以看到,对于复杂的对象,值是一个被proxy拦截处理过的对象,但是里面的属性age和sex不是RefImpl类型的对象,proxy代理的对象同样被挂载到value上,所以可以通过obj.value.age来读取属性,这些属性同样也是响应式的,更改时可以触发视图的更新reactive通过上面ref的使用案例,起始不管是复杂引用类型,如array,object等,亦或者是简单的值类型string,number

  • selenium3降级到selenium2

    先下载了Python3.5.2,用pipinstallselenium安装的是selenium3.0.2,跑原来的脚本一直报错,网上查到可能是selenium版本过高(下载驱动包之后仍然各种报错),所以决定安装selenium2。然后用pipinstallselenium2Collectingselenium3Downloadingselenium-2.53.6命令安装报错:然后用命令easy_install.exepip==9.0.1installselenium2Collectingselenium3Downloadingselenium-2.53.6安装还是报错,然后决定重新安装Python2.7,然后安装selenium2在官网下载了最新Python2.7.13:https://www.python.org/downloads/release/python-2713/。安装完成之后修改环境变量path指向新安装的Python2.7,然后将原来的Python3的exe执行文件修改为Python3.exe(避免Python命令混淆)然后用pipinstallselenium进行安装

  • Maat:自动分析VirusTotal以进行准确标记和有效的恶意软件检测方法(CS CS)

    恶意软件分析和检测研究社区依靠在线平台VirusTotal来基于大约60个抗病毒扫描程序的扫描结果标记Android应用程序。不幸的是,目前尚无关于如何最好地解释从VirusTotal获得的扫描结果的标准,这导致使用不同的基于阈值的标记策略(例如,如果十个或更多的扫描仪认为某个应用程序是恶意的,则被认为是恶意的)。尽管某些使用的阈值可能能够准确估算应用程序的基本情况,但VirusTotal更改其使用的扫描仪的设置和版本这一事实使这种阈值随着时间的推移变得难以为继。我们实施了一种方法Maat,通过自动生成基于机器学习(ML)的标签方案来解决这些标准化和可持续性问题,胜过基于阈值的标记策略。我们使用跨越一年的53K个Android应用程序的VirusTotal扫描报告,通过将Maat基于ML的标签策略与基于阈值的策略的性能进行了比较,评估了它们的适用性。我们发现,这种基于ML的策略(a)可以根据VirusTotal扫描报告准确,一致地标记应用程序,并且(b)有助于训练基于ML的检测方法,该方法比未使用的应用程序更有效地分类基于阈值的对应对象。原文标题:Maat:AutomaticallyA

  • python编码问题

    基本常识 ASCII编码是1个字节bytes,而Unicode编码通常是2个字节 1bytes=8bit 在计算机内存中,统一使用Unicode编码,当需要保存到硬盘或者需要传输的时候,就转换为UTF-8编码。字母"A"用ASCII编码是十进制的65,二进制的01000001; 字符"0"用ASCII编码是十进制的48,二进制的00110000,注意字符'0'和整数0是不同的; 汉字"中"已经超出了ASCII编码的范围,用Unicode编码是十进制的20013,二进制的0100111000101101,1个字节的ASCII编码已经不能满足。 可以猜测,如果把ASCII编码的A用Unicode编码,只需要在前面补0就可以,因此,A的Unicode编码是0000000001000001, 区别:一个是1字节一个是2字节。试想如果你的文本全是英文,用Unicode编码比ASCII编码需要多一倍的存储空间,在存储和传输上就十分不划算,因为Unicode是2个字节,一个字节可以表示完所有的英文字母单词。所以,本着节约

  • 从构建分布式秒杀系统聊聊限流特技

    前言俗话说的好,冰冻三尺非一日之寒,滴水穿石非一日之功,罗马也不是一天就建成的。两周前秒杀案例初步成型,分享到了中国最大的同性交友网站-码云。同时也收到了不少小伙伴的建议和投诉。我从不认为分布式、集群、秒杀这些就应该是大厂的专利,在互联网的今天无论什么时候都要时刻武装自己,只有这样,也许你的春天就在明天。在开发秒杀系统案例的过程中,前面主要分享了队列、缓存、锁和分布式锁以及静态化等等。缓存的目的是为了提升系统访问速度和增强系统的处理能力;分布式锁解决了集群下数据的安全一致性问题;静态化无疑是减轻了缓存以及DB层的压力。限流然而再牛逼的机器,再优化的设计,对于特殊场景我们也是要特殊处理的。就拿秒杀来说,可能会有百万级别的用户进行抢购,而商品数量远远小于用户数量。如果这些请求都进入队列或者查询缓存,对于最终结果没有任何意义,徒增后台华丽的数据。对此,为了减少资源浪费,减轻后端压力,我们还需要对秒杀进行限流,只需保障部分用户服务正常即可。就秒杀接口来说,当访问频率或者并发请求超过其承受范围的时候,这时候我们就要考虑限流来保证接口的可用性,以防止非预期的请求对系统压力过大而引起的系统瘫痪。通常

  • 社交网络之图论实战

    社交网络之图论实战0.前言1.准备工作2.学习方法3.社交网络实战4.问题处理5.作者的话0.前言又到了新的一周,今天来学点新的知识,这节学的知识还是非常重要,那就是属于社交网络方向以及知识图谱方向以及我们研究生的一门课---《图论》!本节将从我的学习方式到英文文档如何学习以及如何处理问题,以及如何去研究社交网络及图论等角度来分析! 下面一起来学习新知识吧,记得打开你的python哦,哈哈,就是一篇python实战篇!1.准备工作本节以python-igraph来学习社交网络与图论相关知识!【两个网站】那么我们一起来安装一下,这里引入两个网站。第一:(whl)包安装网站:https://www.lfd.uci.edu/~gohlke/pythonlibs/第一:igraphAPI官网:http://igraph.org/python/doc/tutorial/tutorial.html#structural-properties-of-graphs【安装包】在第一个网站搜索Python-igraph,然后找到相应的版本下载即可。最后pipinstallxxx.whl即可完成安装!2.

  • 谷歌开发EfficientNets,扩大CNN并与AutoML结合,效率提升10倍|一周AI最火论文

    大数据文摘专栏作品作者:ChristopherDossman编译:笪洁琼、conrad、云舟呜啦啦啦啦啦啦啦大家好,拖更的AIScholarWeekly栏目又和大家见面啦!AIScholarWeekly是AI领域的学术专栏,致力于为你带来最新潮、最全面、最深度的AI学术概览,一网打尽每周AI学术的前沿资讯。每周更新,做AI科研,每周从这一篇开始就够啦! 本周关键词:GoogleAI、深度预测、反欺诈本周热门学术研究人类的本质是复读机,GANs的本质是复制粘贴研究人员从Copy-Pasting(GANs)中得到启发,设计了一种新的对象发现训练程序。在这一新的训练流程中,生成器不会像传统的对象发现方法那样直接生成对象。相反,它会识别并分割现有对象。该方法适用于各种不同的数据集,包括复杂背景下有外观变换的大型对象。研究表明,通过训练Copy-PastingGANs,防止生成模型走捷径,可以实现无监督的对象发现。该方法可以处理来自真实图像的杂乱背景,并且可以在不从头开始的情况下,以更高效的数据方式预先训练用于有监督的对象检测模型。它还可以用作智能代理的视觉模块。基本上,这项工作可以有效地为用户

  • Salesforce中的“Assets”到底是干嘛用的?

    如果你使用了Salesforce一段时间的话,会发现Salesforce有一个叫Assets的标准对象。很多同学在实际项目中通常都会将这个对象隐藏起来,感觉这就是一个鸡肋的功能,但是Assets真的是一个鸡肋吗?Salesforce设计这个功能的逻辑到底是什么呢?今天我们就来了解下这个不招人待见的Assets。总体来说Assets有三个作用:第一,跟踪已销售设备的状态例如客户向你采购了一批笔记本电脑,你需要记录客户的所买的笔记本的序列号,价格,购买时间,购买数量等。如果是大型设备还需要记录设备的安装时间等。有了这些信息我们可以很容易的了解客户购买了我们哪些产品,产品的状态如何等。如图1,在DreamHouseRealty的客户有个关联的对象"资产"(Assets)。我们可以在Asset中跟踪已经销售给客户的太阳能电池板的信息(SolarPanelSystem4000)图1“SolarPanelSystem4000”只是许多产品组件的组合名称,我们还可以跟踪“SolarPanelSystem4000”下面具体的产品组件,如SolarPanel组件,MountingSy

  • 还在为一个推送,加班熬夜早起么?

    最近公司召开员工大会和新年年会,所有人都跟打鸡血一样,但是早晨看到秘书小姐姐,一脸疲惫,感觉整个人都不太好!小姐姐,为什么你看起来如此生无可恋!都不美了!乐乐文文你要是早晨7点不到爬起来,守在电脑前,等着给大家推送员工大会通知,你就知道什么叫生无可恋了!难道你不知道乐享的定时推送么???乐乐定时推送,顾名思义就是可以在提前设置好的时间里,把图文内容推送出去。 再管理后台>>图文消息推送下,点击发送,再选择定时发送就可以啦!比如早晨7点的员工大会通知 比如晚上10点的年会散场安排更重要的是,马上就要过年了,各种节日推送,新年寄语,难道你还要背着电脑,人家在看春晚,你在发推送吗?定时推送还你一个美好的春节假期!好了,这次的推送就是那么简单粗暴,定时推送让你可以提前安排好自己的工作,更自由的支配自己的时间,就酱!白白!

  • ol4加载pbf矢量切片与样式定义

    概述看了一下mapbox的矢量切片的展示方式,其核心是定义的一个样式配置文件,我就在想:Ol4里面我是否通过styleFunction的方式实现同样的效果呢,折腾了一上午,别说,styleFunction真好用,在此分享出来,供大家参看。mapbox的样式配置 如上图所示,mapbox的样式定义是通过一个这样的配置实现的,实现后效果如下: openlayers4的样式配置 如上图所示,我是模仿mapbox的配置文件,并结合ol4的特性做了一部分修改。实现后效果如下: 实现1、矢量切片矢量切片是通过geoserver来实现的。实现可参考博客Geoserver2.11矢量切片与OL3中的调用展示。切片图层是一个layergroup,如下图: 2、样式定义文件{ "province":[ { "type":"polygon", "fill":{ "color":"rgba(255,0,0,0)" }, "stroke":{ "color&quo

  • 提升markdown的中文输入效率

    Markdown这种格式的出现大大提升了写作的效率,但是它对于非英文的用户其实并不友好:每当我们需要使用#[-等标志符的时候,需要不断地切换输入法。首先,切换输入法(就算是按shift键)让我们的思维不连贯;其次,一旦中间有一次切换出错,那么又有撤销的成本;我相信每一个非英文markdown的使用者都有这种困惑;实际想要达到的效果如下:避免输入法切换最简单的办法就是把markdown使用的那些特定字符!-[]#*(),直接使用半角符号代替全角符号;完成这个功能最好的角色是输入法;但目前除了可以定制的鼠须管等能完成,其他的国产输入以及系统输入法都不支持;在第三方输入法支持这个功能之前,我这里给出一个简单的方案。如果你使用鼠须管鼠须管/小狼嚎输入法是可以定制的,如果你是这种输入法的用户,那么恭喜你,实现方式非常简单;修改一下配置即可,具体做法见调整「鼠须管」实现高效的Markdown输入如果你使用Mac如果你使用第三方输入法或者mac的系统输入法,那么我们可以通过修改键盘映射来解决这个问题:把全角的markdown映射为半角符号。具体做法如下:安装Karabiner软件下载地址点这里;按照

  • 朋友们再也不用担心你们的丑照没地方存了!

    喜欢旅游的镁客君从来都不吝啬用相机记录下美好的景色与记忆,只不过技术有限常常从10张里面才能挑出一张可以用的~会拍照的亲来教教镁客君吧!这也就出现了往往照片成灾……尤其是当苹果的LivePhoto一出……手机的内存明显感觉到有些紧张……嫌麻烦的镁客君能用手机解决的都不用单反!可恨的iCloud还要每月付费才可以维持照片的保存与扩大内存……白花花的银子真是心疼啊!于是有朋友给镁客君推荐了一款堪比iCloud的存储神器!刚看到的镁客君还以为这是个充电宝,如此小巧~说它能存储相当多的照片镁客君看在它颜值也算高的份儿上就相信了它!这个叫做Monument小家伙儿使用的是外部USB驱动器存储,也就是说存在相当的灵活性来让我们自己控制内存的大小,如果需要更多的空间就通过添加另一个驱动器或者更换更大的模型。1Disk(1TB)--大概可以存储150000张照片或者70个小时的视频2Disks(2x1TB)--大概可以存储300,000张照片或者140个小时的视频2Disks(2x2TB)-大概可以存储600,000张照片或者280个小时的视频这个计算照片参数是基于入门级单反相机(使用JPEG细设置)

  • “机器换人”需解决技术和市场的矛盾

    还沉迷于科幻电影《Irobot》中的机器人场景?如今,这些正在中国工厂成为现实,更新中国制造。近期有部著名的电影《星际穿越》,里面有个有趣的角色——智能机器人,不仅能够辨别人类、物体、场景和活动,能漫步太空、深海潜水,而且有自己的思想。现实中,这样的机器人在浙江被制造出来了。 2008年国际金融危机后中国用工荒问题突出,机器人开始活跃在市场上。而浙江省作为一个制造业大省、外来打工者重镇,低成本优势已经不再,2012年底浙江省委省政府做出“全面推进机器换人”的决策部署,“机器换人”正在成为推进工业转型升级的重要抓手。 在此市场和政策红利之下,全省正在掀起一股“机器换人”热潮。 按照浙江省政府估算,在三年内规模以上企业基本完成“机器换人”后,浙江全社会劳动生产率将由目前的10万元/人年上升至14万元/人年,意味着将带来上万亿GDP的增长。 面对全球经济萎缩、劳动力成本上升、招工难等问题,“机器换人”能否提供解决之道?机器人产业本身作为一个新兴产业,又面临什么样的机遇与挑战?在这场中国工业化与城镇化转型的变革中,浙江样本又能提供什么样的经验? 而近日中央经济工作会议就指出:“人口老龄

  • HCNP Routing&amp;Switching之MUX VLAN

      前文我们了解了代理ARP相关话题,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/16188230.html;今天我们再来聊一聊vlan隔离相关话题MUXVLAN;   vlan隔离-端口隔离回顾   在同一网段的同一vlan内,为了使各端口互不干扰影响,我们可以使用端口隔离技术,配置交换机某些端口双向隔离或者单向隔离;这是从二层的角度去隔离;但是我们可以使用三层接口,开启代理arp绕过二层端口隔离,于是为了防止代理arp绕过二层端口隔离,我们也可以修改端口隔离模式为all,即二层和三层都隔离;   MUXVLAN   今天聊的MUXVLAN也是一种vlan隔离技术,相对于同VLAN内的端口隔离,它更灵活;它能实现部分VLAN间可以互通、部分VLAN间隔离,同时也可以实现VLAN内端口隔离;它也是通过vlan进行网络资源控制的一种机制,只适用于二层网络中,对同一网段的用户进行隔离(注意,是同一网段)和互通;简单说MUXVLAN实现了处于相同网段的设备划入不同VLAN后,虽然二层通信是隔离的,但可以和指定vlan通信,还可以实现禁止相同VLAN内

  • 链家二手房楼盘爬虫

    前言 想看下最近房价是否能入手,抓取链家二手房、新房的信息,发现广州有些精装修88平米的3房2厅首付只要29万!平均1.1万/平: 查看请求信息 本次用的是火狐浏览器32.0配合firebug和httpfox使用,基于python3环境,前期步骤: 首先打开firefox浏览器,清除网页所有的历史纪录,这是为了防止以前的Cookie影响服务器返回的数据。 F12打开firebug,进入链家手机端首页https://m.lianjia.com,点击网络->头信息,查看请求的头部信息。 发现请求头信息如下,这个是后面要模拟的: Host:m.lianjia.com User-Agent:Mozilla/5.0(WindowsNT6.3;WOW64;rv:32.0)Gecko/20100101Firefox/32.0 Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language:zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3 Accept-

  • sql语句

    判断是否存在:select1fromtablenamewherecol='col'limit1;

  • 设计模式-抽象工厂模式

    模式动机 在工厂方法模式中具体工厂负责生产具体的产品,每一个具体工厂对应一种具体产品,工厂方法也具有唯一性,一般情况下,一个具体工厂只有一个工厂方法或者一组重载的工厂方法。但是有时候我们需要一个工厂可以提供多个产品对象,而不是单一的产品对象。 为了更清晰的理解工厂方法模式,需要先引入两个概念: 产品等级结构:产品等级结构即产品的继承结构,如一个抽象类是电视机,其子类有海尔电视机、海信电视机、TCL电视机,则抽象电视机与具体品牌的电视机之间构成了一个产品等级结构,抽象电视机是父类,而具体品牌的电视机是其子类。 产品族:在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品,如海尔电器工厂生产的海尔电视机、海尔电冰箱,海尔电视机位于电视机产品等级结构中,海尔电冰箱位于电冰箱产品等级结构中。 当系统所提供的工厂所需生产的具体产品并不是一个简单的对象,而是多个位于不同产品等级结构中属于不同类型的具体产品时需要使用抽象工厂模式。 抽象工厂模式是所有形式的工厂模式中最为抽象和最具一般性的一种形态 抽象工厂模式与工厂模式最大的区别在于,工厂方法模式针对的是一个产品

  • 转载:屎人--&gt;诗人系列--码农之歌

    转贴经常关注的一个博主的文,感觉还挺有趣: https://goofegg.github.io/content.html?id=141 ************************** 这个系列第一篇给了大白,第二篇想着写码农。把码农说成屎人,如果他们不看我的那个序的话,那一定是会被他们打的厉害,被码农打,那是不得了的事,虽然不会对我拳脚相加,但是会把我的底透的一点都不剩。在此拜请各位大侠,一定要回头看一下我那个序,我是把所有的人都贴了个屎人的标签的,不是专门针对码农说是屎人,对那些稍稍动动手指就能知道我们每个人的隐私的黑客大侠,我是万万不敢得罪的,在这里我只拱拱手都不能表示我敬重的诚意,再此膜拜了。为什么要写码农?虽然我理解不了他们码的程序,但是我们生活中很多的所用以及社会正常运转的秩序就是来自于他们的工作,所以我特崇拜那些技术大神,而且如果从屎人做的琐碎里找诗意的话,写出来的诗相当于码字,但是在我看来,码农码的程序是更牛B的诗,诗是没有实际用处的,但是程序的运行,却是我们社会运行的灵魂,而且我们这些码农眼里的憨憨虽然对他们仰视到崇拜的程度,但是他们兴奋起来没日没夜的工作和生活状

  • Java多线程通关——基础知识挑战

    等掌握了基础知识之后,才有资格说基础知识没用这样的话。否则就老老实实的开始吧。     对象的监视器每一个Java对象都有一个监视器。并且规定,每个对象的监视器每次只能被一个线程拥有,只有拥有它的线程把它释放之后,这个监视器才会被其它线程拥有。其实就是说,对象的监视器对于多线程来说是互斥的,即一个线程从拿到它之后到释放它之前这段时间内,其它线程是绝对不可能再拿到它的。这是由JVM保证的。这样一来,对象的监视器就可以用来保护那种每次只允许一个线程执行的方法或代码片段,就是我们常说的同步方法或同步代码块。Java包括两种范畴的对象(当然,这样讲可能不准确,主要用于帮助理解),一种就是普通的对象,比如newObject()。一种就是描述类型信息的对象,即Class<?>类型的对象。这两类都是Java对象,这毋庸置疑,所以它们都有监视器。但这两类对象又有明显的不同,所以它们的监视器对外表现的行为也是不同的。请看下面表达式:   Objecto1=newObject();Objecto2=newObject();o1==o2;//false复制 &nbs

  • java ArrayList条件排序

    User类有agenamesex属性 ListuserList=newArrayList<>(); userList.sort(newComparator<User>){ @Override publicintcompare(Usero1,Usero2){ returno1.age-o2.age; } }); //进化到复制 userList.sort((o1,o2)->o1.age-o2.age);复制 //再进化到userList.sort(Compare.comparing(User:age));复制

  • 《我叫赵甲第》片尾句集锦

    《我叫赵甲第》片尾句集锦 第一集: 生活的道路是自由美好的 可是我们迷了路 贪婪毒害了人性,用仇恨分割了世界 ---查理·卓别林《大独裁者》 第二集: 一切都是瞬息,一切都将会过去 而那过去了的,就会成为亲切的怀恋 ---亚历山大·普希金《假如生活欺骗了你》 第三集: 我姑且举灰黑的手装作喝干一杯酒 我将在不知道时候的时候独自远行 ---鲁迅《野草》 第四集: 一旦走进深处,人与人就是相互的迷宫 ---史铁生《活出爱》 第五集: 爱情是叹息吹起的一阵烟 ---威廉·莎士比亚《罗密欧与朱丽叶》 第六集: 当我猜到谜底,才发现 一切都已过去,岁月早已换了谜题 ---席慕容《谜题》 第七集: 即使一切都已发生过,我也习惯了,不再流泪 ---舒婷《路遇》 第八集: 孤独的人有他们自己的泥沼 ---张爱玲《年青的时候》 第九集: 有生命的种子决不会悲观、叹气 它相信有了阻力才有磨炼 ---夏衍《种子的力》 第十集: 我们飞翔得越高 我们在那些不能飞翔的人眼中的形象越是渺小 ---弗里德里希·尼采《查拉图斯特拉如是说》 第十一集: 纵使黑暗吞噬了一切,太阳还可以重新回来。 ---汪国真《只要明天还

相关推荐

推荐阅读