“全栈2019”Java原子操作第七章:AtomicLong介绍与使用
cac55 2024-10-20 04:21 18 浏览 0 评论
难度
初级
学习时间
30分钟
适合人群
零基础
开发语言
Java
开发环境
- JDK v11
- IntelliJIDEA v2018.3
友情提示
- 本教学属于系列教学,内容具有连贯性,本章使用到的内容之前教学中都有详细讲解。
- 本章内容针对零基础或基础较差的同学比较友好,可能对于有基础的同学来说很简单,希望大家可以根据自己的实际情况选择继续看完或等待看下一篇文章。谢谢大家的谅解!
1.温故知新
前面在《“全栈2019”Java原子操作第二章:i++是原子操作吗?何为原子性》一章中介绍了什么是原子性。
在《“全栈2019”Java原子操作第三章:比较并交换CAS技术详解》一章中介绍了什么是比较并交换CAS技术。
在《“全栈2019”Java原子操作第四章:AtomicBoolean介绍与使用》一章中介绍了什么是原子操作类AtomicBoolean。
在《“全栈2019”Java原子操作第五章:AtomicInteger介绍与使用》一章中介绍了什么是原子操作类AtomicInteger。
在《“全栈2019”Java原子操作第六章:AtomicInteger灵活的运算方式》一章中介绍了使用原子操作类AtomicInteger的方法实现更灵活的运算方式。
现在介绍原子操作类AtomicLong。
2.什么是原子操作类?
顾名思义,原子操作类就是实现了原子操作的类。
原子操作的概念和必备知识在该系列的《“全栈2019”Java原子操作第一章:内存可见性volatile关键字解析》和《“全栈2019”Java原子操作第二章:i++是原子操作吗?何为原子性》以及《“全栈2019”Java原子操作第三章:比较并交换CAS技术详解》三章中已经详细介绍过了,这里就不再赘述。不清楚的小伙伴请前去查阅相关章节。
2.AtomicLong简介
AtomicLong是一个以原子方式操作long值的类。
先简单来看一下AtomicLong类长什么样:
这里需要注意的是,AtomicLong和AtomicInteger类一样继承了Number类,说明它不能替代Long类,只是扩展了Number类。
AtomicLong类很简单,它有两个构造方法:
如下:
- AtomicLong()
- AtomicLong?(long initialValue)
这两个构造方法都比较常用。
第二个构造方法AtomicLong?(long initialValue)可以指定初始值。
下面我们就来用一用AtomicLong。
3.AtomicLong应用场景
例如,我们某些应用需要生成序列号或唯一ID时,那么就可以用到AtomicLong。
如果大家有更多关于AtomicLong的应用场景请在评论区留言,谢谢。
下面,来看看未使用AtomicLong的时候会是怎样。
例子是序列号生成器。
首先,我们创建一个序列号生成器类:
然后,将其构造方法进行私有化,目的是不让创建对象,只能使用其静态方法:
接着,定义一个long类型的变量记录当前生成的序列号:
然后,定义一个获取当前序列号的方法:
接着,再定义一个生成序列号的方法:
序列号生成器类写好了。
接下来,我们去试试序列号生成器。
在Main类中创建一个生成序列号任务:
接着,在run方法里面调用生成器类对象的生成序列号的方法并输出结果:
run()方法书写完毕。
然后,我们创建100个线程去生成序列号:
最后,等这100个线程都执行完生成序列号任务以后,我们再次生成序列号:
如果当前生成的序列号是100,则说明每个线程都有自己唯一的序列号,都是不同的;
如果当前生成的序列号不是100,则说明有序列号相同的线程,序列号生成器这个类就有问题。
不过在此之前需要睡1秒钟,目的是等这100个线程执行完毕之后我们再生成:
例子书写完毕。
运行程序,执行结果:
从运行结果来看,符合预期。最后生成的序列号的确为100,看似生成序列号这个类没有问题。
生成序列号这个类真的没有问题吗?
我们修改例子再试试。
在生成序列号任务中,使执行生成序列号方法之前睡100毫秒:
例子改写完毕。
运行程序,执行结果:
从运行结果来看,符合预期。当前序列号不为100,说明生成序列号这个类是有问题的。
怎么解决?
这时我们的AtomicLong类派上用场了。
我们只需用AtomicLong替换long类型即可:
因为AtomicLong替换了long,所以获取序列号方法和生成序列号方法都得改变。
先是获取当前序列号方法:
然后是生成序列号方法:
对于学过前面几章的小伙伴来说,get()方法和getAndIncrement()方法不陌生,get()方法是用来获取sequenceNumber的当前值;getAndIncrement()方法是用来递增sequenceNumber对象值的,相当于i++。
例子改写完毕。
运行程序,执行结果:
从运行结果来看,符合预期。当前序列号为100,生成序列号这个类之前的问题得到了解决。
至此,序列号生成器的问题就先告一段落了。
下面就开始说说AtomicLong类里面几个常用方法及源码。
对于学过《“全栈2019”Java原子操作第五章:AtomicInteger介绍与使用》一章的小伙伴来说,掌握下面的内容轻而易举,因为变化的仅仅是int类型变为long类型。
4.获取get()/设置set?(long newValue)当前值方法
先来看看AtomicLong类这两个方法:
- get()
- set?(long newValue)
下面依次来看看这两个方法。
get()方法在AtomicLong类中的源码:
get()方法的作用是获取AtomicLong对象的当前值;
set?(long newValue)方法在AtomicLong类中的源码:
set?(long newValue)方法的作用是设置AtomicLong对象的当前值。参数newValue是我们可以指定的新值。
接下来,我们来试试get()方法和set?(long newValue)方法。
新例子,不是上一小节的例子。
首先,创建出AtomicLong对象并指定初始值:
然后,调用value的get()方法获取当前值:
接着,调用value的set?(long newValue)方法指定新值:
最后,调用value的get()方法再次获取当前值,看新值是否被设置成功:
例子书写完毕。
运行程序,执行结果:
从运行结果来看,符合预期。值从0变化为1。
5.递增方法
AtomicLong类中有两个递增方法:
- getAndIncrement()
- incrementAndGet()
其中,getAndIncrement()方法相当于i++,incrementAndGet()方法相当于++i。
我们来依次看看这两个方法。
getAndIncrement()方法在AtomicLong类中的源码:
getAndIncrement()方法的作用是返回递增前的值。
incrementAndGet()方法在AtomicLong类中的源码:
incrementAndGet()方法的作用是返回递增后的值。
接下来,我们来试试getAndIncrement()方法和incrementAndGet()方法。
新例子,不是上一小节的例子。
首先,我们创建一个AtomicLong对象并指定初始值:
然后,调用getAndIncrement()方法:
最后,调用incrementAndGet()方法:
例子书写完毕。
运行程序,执行结果:
从运行结果来看,符合预期。
首先调用getAndIncrement()方法时,value的值为0,根据getAndIncrement()方法的先返回当前值再递增的原则,第一次输出结果正确;
getAndIncrement()方法递增完之后,value的值为1;
接下来,调用incrementAndGet()方法时,value值为1,根据incrementAndGet()方法的先递增再返回当前值的原则,第二次输出结果也正确;
incrementAndGet()方法先递增的结果就是:1被递增之后变为2。
说完AtomicLong类中的递增方法,再来说说AtomicLong类中的递减方法。
6.递减方法
AtomicLong类中有两个递减方法:
- getAndDecrement()
- decrementAndGet()
其中,getAndDecrement()方法相当于i--,decrementAndGet()方法相当于--i。
我们来依次看看这两个方法。
getAndDecrement()方法在AtomicLong类中的源码:
getAndDecrement()方法的作用是返回递减前的值。
decrementAndGet()方法在AtomicLong类中的源码:
decrementAndGet()方法的作用是返回递减后的值。
接下来,我们来试试getAndDecrement()方法和decrementAndGet()方法。
新例子,不是上一小节的例子。
首先,我们创建一个AtomicLong对象并指定初始值:
然后,调用getAndDecrement()方法:
最后,调用decrementAndGet()方法:
例子书写完毕。
运行程序,执行结果:
从运行结果来看,符合预期。
首先调用getAndDecrement()方法时,value的值为0,根据getAndDecrement()方法的先返回当前值再递减的原则,第一次输出结果正确;
getAndDecrement()方法递减完之后,value的值为-1;
接下来,调用decrementAndGet()方法时,value值为-1,根据decrementAndGet()方法的先递减再返回当前值的原则,第二次输出结果也正确;
decrementAndGet()方法先递减的结果就是:-1被递减之后结果为-2。
7.加上任意值方法
前面两小节演示的是自增和自减情况,如果想要加上任意数,比如+5,或者你想要减去任意数,比如-5,使用前面那几个方法是做不到的,所以AtomicLong类也为我们提供相应方法。
AtomicLong类中有两个加上任意值的方法:
- getAndAdd(long delta)
- addAndGet(long delta)
如果你是做加法,那么就直接写一个正数即可;如果你是想做减法,那么就直接写一个负数即可。
我们来依次看看这两个方法。
getAndAdd(long delta)方法在AtomicLong类中的源码:
getAndAdd(long delta)方法的作用是返回加上任意值前的值。
addAndGet(long delta)方法在AtomicLong类中的源码:
addAndGet(long delta)方法的作用是返回加上任意值后的值。
接下来,我们来试试getAndAdd(long delta)方法和addAndGet(long delta)方法。
新例子,不是上一小节的例子。
首先,我们创建一个AtomicLong对象并指定初始值:
然后,调用getAndAdd(long delta)方法:
最后,调用addAndGet(long delta)方法:
例子书写完毕。
运行程序,执行结果:
从运行结果来看,符合预期。
首先调用getAndAdd(long delta)方法时,value的值为0,根据getAndAdd(long delta)方法的先返回当前值再加上任意值的原则,第一次输出结果正确;
getAndAdd(long delta)方法加完任意值之后,value的值为3;
接下来,调用addAndGet(long delta)方法时,value值为3,根据addAndGet(long delta)方法的先加上任意值再返回当前值的原则,第二次输出结果也正确;
addAndGet(long delta)方法先加上任意值的结果就是:3加上-1结果为2。
8.自定义更新方式
前面几个小节演示的都是和指定数的加法或减法,如果我想自定义更新value的值怎么办呢?
AtomicLong类为我们提供了两个可以自定义更新value值的方法:
- getAndUpdate(LongUnaryOperator updateFunction)
- updateAndGet(LongUnaryOperator updateFunction)
怎么更新呢?
你只需传入LongUnaryOperator接口的实现即可。
我们先来看看LongUnaryOperator接口:
LongUnaryOperator接口里面我们只需实现applyAsLong(long operand)方法即可:
applyAsLong(long operand)方法中的值是我们调用getAndUpdate(LongUnaryOperator updateFunction)方法或updateAndGet(LongUnaryOperator updateFunction)方法时传递过去的:
下面,我们来依次看看这两个方法。
getAndUpdate(LongUnaryOperator updateFunction)方法在AtomicLong类中的源码:
getAndUpdate(LongUnaryOperator updateFunction)方法的作用是返回更新前的值。
updateAndGet(LongUnaryOperator updateFunction)方法在AtomicLong类中的源码:
updateAndGet(LongUnaryOperator updateFunction)方法的作用是返回更新后的值。
接下来,我们来试试getAndUpdate(LongUnaryOperator updateFunction)方法和updateAndGet(LongUnaryOperator updateFunction)方法。
新例子,不是上一小节的例子。
首先,我们创建一个AtomicLong对象并指定初始值:
然后,调用getAndUpdate(LongUnaryOperator updateFunction)方法:
最后,调用updateAndGet(LongUnaryOperator updateFunction)方法:
例子书写完毕。
运行程序,执行结果:
从运行结果来看,符合预期。
首先调用getAndUpdate(LongUnaryOperator updateFunction)方法时,value的值为0,根据getAndUpdate(LongUnaryOperator updateFunction)方法的先返回当前值再更新为指定值的原则,第一次输出结果正确;
getAndUpdate(LongUnaryOperator updateFunction)方法更新为指定值之后,value的值为1,即 0 + 1 = 1;
接下来,调用updateAndGet(LongUnaryOperator updateFunction)方法时,value值为1,根据updateAndGet(LongUnaryOperator updateFunction)方法的先更新为指定值再返回当前值的原则,第二次输出结果也正确;
updateAndGet(LongUnaryOperator updateFunction)方法先更新为指定值的结果就是:1 * 2 = 2。
9.使用自定义运算方式更新当前值
如果我们想用AtomicLong对象以原子的方式做更高级更灵活的更新方式,那么最好是使用getAndAccumulate(long x,LongBinaryOperator accumulatorFunction)方法和accumulateAndGet(long x,LongBinaryOperator accumulatorFunction)方法。
下面,我们来依次看看这两个方法。
getAndAccumulate(long x,LongBinaryOperator accumulatorFunction)方法在AtomicLong类中的源码:
getAndAccumulate(long x,LongBinaryOperator accumulatorFunction)方法的作用是返回使用自定义运算方式更新前的值。
accumulateAndGet(long x,LongBinaryOperator accumulatorFunction)方法在AtomicLong类中的源码:
accumulateAndGet(long x,LongBinaryOperator accumulatorFunction)方法的作用是返回使用自定义运算方式更新后的值。
getAndAccumulate(long x,LongBinaryOperator accumulatorFunction)方法和accumulateAndGet(long x,LongBinaryOperator accumulatorFunction)方法中都需要一个LongBinaryOperator类型参数。
LongBinaryOperator是一个long类型的二元运算接口,它里面只有一个applyAsLong(long left, long right)方法:
applyAsLong(long left, long right)方法中有两个参数:left和right。
left代表左边的操作数;
right代表右边的操作数;
什么意思?
比如,x.applyAsLong(2,3),那么left = 2,right = 3。在AtomicLong类中,left = 原值,rigth = 调用getAndAccumulate(long x,LongBinaryOperator accumulatorFunction)方法和accumulateAndGet(long x,LongBinaryOperator accumulatorFunction)方法中的参数long x。
接下来,我们来试试getAndAccumulate(long x,LongBinaryOperator accumulatorFunction)方法和accumulateAndGet(long x,LongBinaryOperator accumulatorFunction)方法。
新例子,不是上一小节的例子。
首先,创建出AtomicLong对象并指定初始值,初始值为2:
然后,调用getAndAccumulate(long x,LongBinaryOperator accumulatorFunction)方法,在applyAsLong(long left, long right)方法中,我们要做乘法运算,也就是left*right:
接着,调用accumulateAndGet(long x,LongBinaryOperator accumulatorFunction)方法,同上面一样,在applyAsLong(long left, long right)方法中,我们也要做乘法运算,也就是left*right:
例子书写完毕。
运行程序,执行结果:
从运行结果来看,符合预期。
第一次运算时返回的是当前值,也就是还没运算过的原值,即2。
第一次做的是乘法运算,left = 2,right = 3,结果 = 6;
第二次运算时返回的是运算后的值,其中left = 6,right = 2;
第二次做的也是乘法运算,left * right = 6 * 2 = 12。
整个运算结果完全正确。
那么,使用getAndAccumulate(long x,LongBinaryOperator accumulatorFunction)方法和accumulateAndGet(long x,LongBinaryOperator accumulatorFunction)方法完成更加灵活的运算完全不在话下。
10.CAS算法体现
我们之前在《“全栈2019”Java原子操作第三章:比较并交换CAS技术详解》一章中学习过什么是CAS算法。
在AtomicLong类中也有体现:
compareAndSet(long expectedValue, long newValue)方法的作用是如果当value==expectedValue,那么就将newValue赋给value,否则什么也不做。
下面我们来试试compareAndSet(long expectedValue, long newValue)方法。
新例子,不是上一小节的例子。
首先,我们创建一个AtomicLong对象并指定初始值,初始值为0:
然后,在调用compareAndSet(long expectedValue, long newValue)方法之前获取一次value的值:
接着,调用compareAndSet(long expectedValue, long newValue)方法并输出方法返回结果:
最后,我们再获取一次value的值看发生变化没有:
例子改写完毕。
运行程序,执行结果:
从运行结果来看,符合预期。
当然了,你也可以将预期值改为一个非原值的数,这样赋值就不成功。
最后,希望大家可以把这个例子照着写一遍,然后再自己默写一遍,方便以后碰到类似的面试题可以轻松应对。
祝大家编码愉快!
GitHub
本章程序GitHub地址:https://github.com/gorhaf/Java2019/tree/master/Thread/atomic/AtomicLong
总结
- AtomicLong是一个以原子方式操作long值的类。
- get()方法的作用是获取AtomicLong对象的当前值;
- set?(long newValue)方法的作用是设置AtomicLong对象的当前值。参数newValue是我们可以指定的新值。
- getAndIncrement()方法的作用是返回递增前的值。
- incrementAndGet()方法的作用是返回递增后的值。
- getAndDecrement()方法的作用是返回递减前的值。
- decrementAndGet()方法的作用是返回递减后的值。
- getAndAdd(long delta)方法的作用是返回加上任意值前的值。
- addAndGet(long delta)方法的作用是返回加上任意值后的值。
- getAndUpdate(LongUnaryOperator updateFunction)方法的作用是返回更新前的值。
- updateAndGet(LongUnaryOperator updateFunction)方法的作用是返回更新后的值。
- getAndAccumulate(long x,LongBinaryOperator accumulatorFunction)方法的作用是返回使用自定义运算方式更新前的值。
- accumulateAndGet(long x,LongBinaryOperator accumulatorFunction)方法的作用是返回使用自定义运算方式更新后的值。
- compareAndSet(long expectedValue, long newValue)方法的作用是如果当value==expectedValue,那么就将newValue赋给value,否则什么也不做。
至此,Java中AtomicLong相关内容讲解先告一段落,更多内容请持续关注。
答疑
如果大家有问题或想了解更多前沿技术,请在下方留言或评论,我会为大家解答。
上一章
“全栈2019”Java原子操作第六章:AtomicInteger灵活的运算方式
下一章
“全栈2019”Java原子操作第八章:AtomicReference介绍与使用
学习小组
加入同步学习小组,共同交流与进步。
- 方式一:关注头条号Gorhaf,私信“Java学习小组”。
- 方式二:关注公众号Gorhaf,回复“Java学习小组”。
全栈工程师学习计划
关注我们,加入“全栈工程师学习计划”。
版权声明
原创不易,未经允许不得转载!
相关推荐
- 服务器用的CPU和个人电脑用的CPU有什么区别?一篇文章告诉你!
-
服务器cpu和普通cpu的区别你的电脑CPU是‘短跑健将’,服务器CPU却是‘铁人三项选手’——它不追求瞬间爆发力,而要7×24小时扛住千军万马的数据洪流!想知道为什么企业机房敢收天价服务费?答案全藏...
- “吃鸡”新版本第1天,玩家进入游戏点击“立即更新”,后悔了!
-
欢迎诸位小伙伴们来到天哥开讲的《和平精英》“精英小课堂”~每逢两三个月,这款游戏就会迎来一次大版本迭代更新,很多朋友会在第一时间更新版本,前往全新的主题模式里一探究竟。不过也有一些老玩家并不会立刻更新...
- 中关村在线·aigo存储杯《无畏契约》全国高校争霸赛招募启事
-
以青春之名,燃电竞之火1赛事背景与宗旨在金秋送爽的9月,芊芊学子们即将回归校园生活。为了给精彩的校园生活锦上添花,由中关村在线与aigo存储联合主办的《无畏契约》全国高校争霸赛正式启幕,旨在为全国高...
- 【生肖狗】9.7-9.10提醒:人算不如天算,转变即是转机
-
九月上旬的风,带着秋意的清爽,也带着几分不可捉摸的变数。对于生肖狗的朋友们来说,9月7日到9月10日这四天,格外需要留意“计划与变化”的碰撞——你们向来习惯提前规划,做事稳妥周全...
- 转转客服IM系统的WebSocket集群架构设计和部署方案
-
本文由转转技术李帅分享,原题“转转客服IM的WebSocket集群部署方案”,下文有修订和重新排版。1、引言转转作为国内头部的二手闲置交易平台,拥有上亿的用户。用户在使用转转app遇到问题时,一般可以...
- 上线3天Steam好评率86%,《时间旅者:重生曙光》开启生存恐怖新篇章
-
这里究竟发生了什么?末日降临,真正的故事悄然启幕。目前,生存恐怖类游戏《时间旅者:重生曙光(Cronos:TheNewDawn)》已在PC(Steam、EpicGamesStore)、P...
- 什么神仙洗衣机让我一天有28小时?拆开松下「大四洗」藏了啥秘密
-
说起家庭洗衣的烦恼,想必很多人都有过类似的经历:贴身内衣要单独洗,宝宝的口水巾得小心呵护,宠物玩具怕藏污纳垢,床单被套又体积庞大,把这些东西混在一起洗担心越洗越脏,分开洗又得反复操作,洗完烘、烘完再洗...
- 爆料人挖出GTA6注册的奇葩域名 延续经典讽刺风格
-
等待《侠盗猎车手6》的日子跨越了数个春秋,在游戏圈期盼着这部可能成为史上最重磅游戏的过程中,每过一段时间就会有些许消息浮出水面。最新线索来自数据挖掘者Tez2在GTA论坛的发现,他可能偶然发现了关于...
- 跟着故事去旅行——读《驼峰间:旅行、探险与征服》
-
作者:郭冰茹《驼峰间》记录了旅行家伊本·白图泰有生之年流传的一则寓言,说一对父子被关进了监狱,有一天儿子问父亲他们每天吃的都是些什么肉,父亲说有牛、羊和骆驼,并且详细地描述了每种动物的特点。但不管父亲...
- 前端工程师需要熟悉的Linux服务器(SSH 终端操作)指令
-
在Linux服务器管理中,SSH(SecureShell)是远程操作的核心工具。以下是SSH终端操作的常用命令和技巧,涵盖连接、文件操作、系统管理等场景:一、SSH连接服务器1.基本连接...
- 跳票6年后,「丝之歌」首发把Steam服务器干爆了 | 玩点好的
-
文丨果脯樱花隧道昨天晚上22点,「鸽」了6年的《空洞骑士:丝之歌》终于上线,算是了却不少玩家的执念。毕竟,这款游戏实在让人等了太多太多年,而且曾有过多次定档后跳票的「案底」,不知道把多少人都整出了P...
- 对标魔兽失败!腾讯版“魔兽”运营一年多后,宣布国际服凉凉
-
大家好,这里是正惊游戏,我是正惊小弟。有很多游戏都想干掉《魔兽世界》,但是大部分魔兽杀手都知道自己不是魔兽的对手,不过是想蹭一下人气而已。腾讯也有一款曾经想对标魔兽的大作,可是上线才一年半国际服就宣布...
- 408 Request Timeout:服务器等待客户端发送请求的时间过长。
-
408RequestTimeout是HTTP状态码之一,表示客户端在发送请求时,服务器等待的时间过长,最终放弃了处理该请求。此问题通常与网络延迟、客户端配置、服务器设置或者应用程序的性能有关...
- 梦幻西游:9.9维护解读,全新时间服锁定129级
-
梦幻西游:9.9维护解读,全新时间服锁定129级9月9日维护解读。1、教师节活动开启,一共7天。挂机,答题,收笔墨纸砚,收海马,搞起来。或者是提前收点家具,教师节期间体力珍贵,家具会涨价。又或者是教师...
- 只是拆掉一面墙,空间就立马大变样,这种设计思路,值得学习
-
你有没有过这样的经历?刚买的房子户型图看起来方方正正,装修完却发现——玄关鞋柜只能塞在角落,进门就撞墙;餐厅正好在过道中间,吃饭像走流程;明明有四个房间,却有一个空着没用,像块食之无味的鸡肋;客餐厅之...
你 发表评论:
欢迎- 一周热门
- 最近发表
-
- 服务器用的CPU和个人电脑用的CPU有什么区别?一篇文章告诉你!
- “吃鸡”新版本第1天,玩家进入游戏点击“立即更新”,后悔了!
- 中关村在线·aigo存储杯《无畏契约》全国高校争霸赛招募启事
- 【生肖狗】9.7-9.10提醒:人算不如天算,转变即是转机
- 转转客服IM系统的WebSocket集群架构设计和部署方案
- 上线3天Steam好评率86%,《时间旅者:重生曙光》开启生存恐怖新篇章
- 什么神仙洗衣机让我一天有28小时?拆开松下「大四洗」藏了啥秘密
- 爆料人挖出GTA6注册的奇葩域名 延续经典讽刺风格
- 跟着故事去旅行——读《驼峰间:旅行、探险与征服》
- 前端工程师需要熟悉的Linux服务器(SSH 终端操作)指令
- 标签列表
-
- 如何绘制折线图 (52)
- javaabstract (48)
- 新浪微博头像 (53)
- grub4dos (66)
- s扫描器 (51)
- httpfile dll (48)
- ps实例教程 (55)
- taskmgr (51)
- s spline (61)
- vnc远程控制 (47)
- 数据丢失 (47)
- wbem (57)
- flac文件 (72)
- 网页制作基础教程 (53)
- 镜像文件刻录 (61)
- ug5 0软件免费下载 (78)
- debian下载 (53)
- ubuntu10 04 (60)
- web qq登录 (59)
- 笔记本变成无线路由 (52)
- flash player 11 4 (50)
- 右键菜单清理 (78)
- cuteftp 注册码 (57)
- ospf协议 (53)
- ms17 010 下载 (60)