志在指尖
用双手敲打未来

2020年多线程面试题预备总结

1、多线程有什么用?
1)挥多核CPU的优势
随着工业的前进,现在的笔记本、台式机乃至商用的运用服务器至少也都是双核的,4核、8核乃至16核的也都不少见,假如是单线程的程序,那么在双核CPU上就浪费了50%,在4核CPU上就浪费了75%。单核CPU上所谓的”多线程”那是假的多线程,同一时刻处理器只会处理一段逻辑,只不过线程之间切换得比较快,看着像多个线程”一起”运转罢了。多核CPU上的多线程才是实在的多线程,它能让你的多段逻辑一起作业,多线程,能够实在发挥出多核CPU的优势来,到达充分利用CPU的意图。
2)防止堵塞
从程序运转功率的视点来看,单核CPU不但不会发挥出多线程的优势,反而会因为在单核CPU上运转多线程导致线程上下文的切换,而下降程序全体的功率。可是单核CPU咱们仍是要运用多线程,便是为了防止堵塞。试想,假如单核CPU运用单线程,那么只需这个线程堵塞了,比方说长途读取某个数据吧,对端迟迟未回来又没有设置超时时刻,那么你的整个程序在数据回来回来之前就中止运转了。多线程能够防止这个问题,多条线程一起运转,哪怕一条线程的代码履行读取数据堵塞,也不会影响其它使命的履行。
3)便于建模
这是另外一个没有这么明显的优点了。假定有一个大的使命A,单线程编程,那么就要考虑很多,树立整个程序模型比较费事。可是假如把这个大的使命A分解成几个小使命,使命B、使命C、使命D,别离树立程序模型,并经过多线程别离运转这几个使命,那就简略很多了。
详细内容篇幅较长共485页,20个技能点,1000道面试题.Java
下面截取部分问题展示,需求完好文档的看最下面.
2、线程和进程的差异是什么?
进程和线程的首要不同在于它们是不同的操作体系资源管理办法。
进程有独立的地址空间,一个进程崩溃后,在保护形式下不会对其它进程发生影响,而线程仅仅一个进程中的不同履行路径。
线程有自己的仓库和局部变量,但线程之间没有独自的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,消耗资源较大,功率要差一些。
但关于一些要求一起进行而且又要同享某些变量的并发操作,只能用线程,不能用进程。
3、Java完结线程有哪几种办法?
1、承继Thread类完结多线程
2、完结Runnable接口办法完结多线程
3、运用ExecutorService、Callable、Future完结有回来成果的多线程
4、发动线程办法start()和run()有什么差异?
只需调用了start()办法,才会表现出多线程的特性,不同线程的run()办法里边的代码替换履行。假如仅仅调用run()办法,那么代码仍是同步履行的,必须等候一个线程的run()办法里边的代码悉数履行完毕之后,另外一个线程才干够履行其run()办法里边的代码。
5、怎样中止一个线程?怎样优雅地中止线程?
stop中止,不推荐。
6、一个线程的生命周期有哪几种状况?它们之间怎样流通的?
NEW:毫无疑问表明的是刚创立的线程,还没有开端发动。
RUNNABLE:表明线程现已触发start()办法调用,线程正式发动,线程处于运转中状况。
BLOCKED:表明线程堵塞,等候获取锁,如碰到synchronized、lock等关键字等占用临界区的状况,一旦获取到锁就进行RUNNABLE状况持续运转。
WAITING:表明线程处于无限制等候状况,等候一个特殊的工作来从头唤醒,如经过wait()办法进行等候的线程等候一个notify()或许notifyAll()办法,经过join()办法进行等候的线程等候方针线程运转完毕而唤醒,一旦经过相关工作唤醒线程,线程就进入了RUNNABLE状况持续运转。
TIMED_WAITING:表明线程进入了一个有时限的等候,如sleep(3000),等候3秒后线程从头进行RUNNABLE状况持续运转。
TERMINATED:表明线程履行完毕后,进行中止状况。需求留意的是,一旦线程经过start办法发动后就再也不能回到初始NEW状况,线程中止后也不能再回到RUNNABLE状况
7、线程中的wait()和sleep()办法有什么差异?
这个问题常问,sleep办法和wait办法都能够用来抛弃CPU必定的时刻,不同点在于假如线程持有某个目标的监视器,sleep办法不会抛弃这个目标的监视器,wait办法会抛弃这个目标的监视器
8、多线程同步有哪几种办法?
Synchronized关键字,Lock锁完结,分布式锁等。
9、什么是死锁?怎样防止死锁?
死锁便是两个线程彼此等候对方开释目标锁。
10、多线程之间怎样进行通信?
wait/notify
11、线程怎样拿到回来成果?
完结Callable接口。
12、violatile关键字的作用?
一个十分重要的问题,是每个学习、运用多线程的Java程序员都必须掌握的。了解volatile关键字的作用的条件是要了解Java内存模型,这儿就不讲Java内存模型了,能够拜见第31点,volatile关键字的作用首要有两个:
1、多线程首要环绕可见性和原子性两个特性而展开,运用volatile关键字润饰的变量,保证了其在多线程之间的可见性,即每次读取到volatile变量,必定是最新的数据
2、代码底层履行不像咱们看到的高档语言—-Java程序这么简略,它的履行是Java代码–>字节码–>依据字节码履行对应的C/C++代码–>C/C++代码被编译成汇编语言–>和硬件电路交互,现实中,为了获取更好的功能JVM或许会对指令进行重排序,多线程下或许会呈现一些意想不到的问题。运用volatile则会对制止语义重排序,当然这也必定程度上下降了代码履行功率从实践视点而言,volatile的一个重要作用便是和CAS结合,保证了原子性,详细的能够拜见java.util.concurrent.atomic包下的类,比如AtomicInteger。
13、新建T1、T2、T3三个线程,怎样保证它们按次序履行?
用join办法。
14、怎样操控同一时刻只需3个线程运转?
用Semaphore。
15、为什么要运用线程池?
咱们知道不用线程池的话,每个线程都要经过newThread(xxRunnable).start()的办法来创立并运转一个线程,线程少的话这不会是问题,而实在环境或许会敞开多个线程让体系和程序到达最佳功率,当线程数到达必定数量就会耗尽体系的CPU和内存资源,也会构成GC频频搜集和中止,因为每次创立和毁掉一个线程都是要消耗体系资源的,假如为每个使命都创立线程这无疑是一个很大的功能瓶颈。所以,线程池中的线程复用极大节省了体系资源,当线程一段时刻不再有使命处理时它也会主动毁掉,而不会长驻内存。
16、常用的几种线程池并讲讲其间的作业原理。
什么是线程池?
很简略,简略看姓名就知道是装有线程的池子,咱们能够把要履行的多线程交给线程池来处理,和连接池的概念一样,经过维护必定数量的线程池来到达多个线程的复用。
线程池的优点
咱们知道不用线程池的话,每个线程都要经过newThread(xxRunnable).start()的办法来创立并运转一个线程,线程少的话这不会是问题,而实在环境或许会敞开多个线程让体系和程序到达最佳功率,当线程数到达必定数量就会耗尽体系的CPU和内存资源,也会构成GC频频搜集和中止,因为每次创立和毁掉一个线程都是要消耗体系资源的,假如为每个使命都创立线程这无疑一个很大的功能瓶颈。所以,线程池中的线程复用极大节省了体系资源,当线程一段时刻不再有使命处理时它也会主动毁掉,而不会长驻内存。
线程池中心类
在java.util.concurrent包中咱们能找到线程池的界说,其间ThreadPoolExecutor是咱们线程池中心类,首要看看线程池类的首要参数有哪些。
怎样提交线程
如能够先随意界说一个固定巨细的线程池
ExecutorServicees=Executors.newFixedThreadPool(3);
提交一个线程
es.submit(xxRunnble);
es.execute(xxRunnble);
submit和execute别离有什么差异呢?
execute没有回来值,假如不需求知道线程的成果就运用execute办法,功能会好很多。submit回来一个Future目标,假如想知道线程成果就运用submit提交,而且它能在主线程中经过Future的get办法捕获线程中的反常。
怎样封闭线程池
es.shutdown();
不再承受新的使命,之条件交的使命等履行完毕再封闭线程池。
es.shutdownNow();
不再承受新的使命,企图中止池中的使命再封闭线程池,回来一切未处理的线程list列表。
17、线程池发动线程submit()和execute()办法有什么不同?
execute没有回来值,假如不需求知道线程的成果就运用execute办法,功能会好很多。submit回来一个Future目标,假如想知道线程成果就运用submit提交,而且它能在主线程中经过Future的get办法捕获线程中的反常。
18、CyclicBarrier和CountDownLatch的差异?
两个看上去有点像的类,都在java.util.concurrent下,都能够用来表明代码运转到某个点上,二者的差异在于:
1、CyclicBarrier的某个线程运转到某个点上之后,该线程即中止运转,直到一切的线程都到达了这个点,一切线程才从头运转;CountDownLatch则不是,某线程运转到某个点上之后,仅仅给某个数值-1罢了,该线程持续运转
2、CyclicBarrier只能唤起一个使命,CountDownLatch能够唤起多个使命
3、CyclicBarrier可重用,CountDownLatch不行重用,计数值为0该CountDownLatch就不行再用了
19、什么是活锁、饥饿、无锁、死锁?
死锁、活锁、饥饿是关于多线程是否活泼呈现的运转堵塞妨碍问题,假如线程呈现了
这三种状况,即线程不再活泼,不能再正常地履行下去了。
死锁
死锁是多线程中最差的一种状况,多个线程彼此占用对方的资源的锁,而又彼此等对方开释锁,此刻若无外力干涉,这些线程则一向处理堵塞的假死状况,构成死锁。
举个比如,A同学抢了B同学的钢笔,B同学抢了A同学的书,两个人都彼此占用对方的东西,都在让对方先还给自己自己再还,这样一向争论下去等候对方还而又得不到处理,老师知道此过后就让他们彼此还给对方,这样在外力的干涉下他们才处理,当然这仅仅个比如没有老师他们也能很好处理,计算机不像人假如发现这种状况没有外力干涉仍是会一向堵塞下去的。
活锁
活锁这个概念我们应该很少有人听说或了解它的概念,而在多线程中这的确存在。活锁恰恰与死锁相反,死锁是我们都拿不到资源都占用着对方的资源,而活锁是拿到资源却又彼此开释不履行。当多线程中呈现了彼此推让,都主动将资源开释给其他线程运用,这样这个资源在多个线程之间跳动而又得不到履行,这便是活锁。
饥饿
咱们知道多线程履行中有线程优先级这个东西,优先级高的线程能够插队并优先履行,这样假如优先级高的线程一向抢占优先级低线程的资源,导致低优先级线程无法得到履行,这便是饥饿。当然还有一种饥饿的状况,一个线程一向占着一个资源不放而导致其他线程得不到履行,与死锁不同的是饥饿在以后一段时刻内仍是能够得到履行的,如那个占用资源的线程完毕了并开释了资源。
无锁
无锁,即没有对资源进行锁定,即一切的线程都能拜访并修正同一个资源,但一起只需一个线程能修正成功。无锁典型的特色便是一个修正操作在一个循环内进行,线程会不断的尝试修正同享资源,假如没有冲突就修正成功并退出不然就会持续下一次循环尝试。所以,假如有多个线程修正同一个值必定会有一个线程能修正成功,而其他修正失利的线程会不断重试直到修正成功。之前的文章我介绍过JDK的CAS原理及运用便是无锁的完结。
能够看出,无锁是一种十分杰出的规划,它不会呈现线程呈现的跳跃性问题,锁运用不当必定会呈现体系功能问题,尽管无锁无法全面替代有锁,但无锁在某些场合下是十分高效的。
20、什么是原子性、可见性、有序性?
原子性、可见性、有序性是多线程编程中最重要的几个知识点,因为多线程状况复杂,怎样让每个线程能看到正确的成果,这是十分重要的。
原子性
原子性是指一个线程的操作是不能被其他线程打断,同一时刻只需一个线程对一个变量进行操作。在多线程状况下,每个线程的履行成果不受其他线程的搅扰,比如说多个线程一起对同一个同享成员变量n++100次,假如n初始值为0,n最后的值应该是100,所以说它们是互不搅扰的,这便是传说的中的原子性。但n++并不是原子性的操作,要运用AtomicInteger保证原子性。
可见性
可见性是指某个线程修正了某一个同享变量的值,而其他线程是否能够看见该同享变量修正后的值。在单线程中必定不会有这种问题,单线程读到的必定都是最新的值,而在多线程编程中就不必定了。
每个线程都有自己的作业内存,线程先把同享变量的值从主内存读到作业内存,构成一个副本,当计算完后再把副本的值刷回主内存,从读取到最后刷回主内存这是一个进程,当还没刷回主内存的时分这时分对其他线程是不行见的,所以其他线程从主内存读到的值是修正之前的旧值。像CPU的缓存优化、硬件优化、指令重排及对JVM编译器的优化,都会呈现可见性的问题。
有序性
咱们都知道程序是按代码次序履行的,关于单线程来说的确是如此,但在多线程状况下就不是如此了。为了优化程序履行和提高CPU的处理功能,JVM和操作体系都会对指令进行重排,也就说前面的代码并不必定都会在后面的代码前面履行,即后面的代码或许会插到前面的代码之前履行,只需不影响当时线程的履行成果。所以,指令重排只会保证当时线程履行成果共同,但指令重排后势必会影响多线程的履行成果。尽管重排序优化了功能,但也是会遵守一些规矩的,并不能随意乱排序,仅仅重排序会影响多线程履行的成果。
什么是看护线程?
与看护线程相对应的便是用户线程,看护线程便是看护用户线程,当用户线程悉数履行完完毕之后,看护线程才会跟着完毕。也便是看护线程必须伴随着用户线程,假如一个运用内只存在一个看护线程,没有用户线程,看护线程自然会退出。
22、一个线程运转时发作反常会怎样?
假如反常没有被捕获该线程将会中止履行。Thread.UncaughtExceptionHandler是用于处理未捕获反常构成线程忽然中止状况的一个内嵌接口。当一个未捕获反常将构成线程中止的时候JVM会使用Thread.getUncaughtExceptionHandler()来查询线程的UncaughtExceptionHandler并将线程和异常作为参数传递给handler的uncaughtException()办法进行处理。
23、线程yield()办法有什么用?
Yield办法能够暂停当时正在履行的线程目标,让其它有相同优先级的线程履行。它是一个静态办法而且只保证当时线程抛弃CPU占用而不能保证使其它线程必定能占用CPU,履行yield()的线程有或许在进入到暂停状况后立刻又被履行。
24、什么是重入锁?
所谓重入锁,指的是以线程为单位,当一个线程获取目标锁之后,这个线程能够再次获取本目标上的锁,而其他的线程是不行以的。
25、Synchronized有哪几种用法?
锁类、锁办法、锁代码块。
26、Fork/Join结构是干什么的?
大使命主动分散小使命,并发履行,合并小使命成果。
27、线程数过多会构成什么反常?
线程过多会构成栈溢出,也有或许会构成堆反常。
28、说说线程安全的和不安全的调集。
Java中平时用的最多的Map调集便是HashMap了,它是线程不安全的。
看下面两个场景:
1、当用在办法内的局部变量时,局部变量归于当时线程级其他变量,其他线程拜访不了,所以这时也不存在线程安全不安全的问题了。
2、当用在单例目标成员变量的时分呢?这时分多个线程过来拜访的便是同一个HashMap了,对同个HashMap操作这时分就存在线程安全的问题了。
29、什么是CAS算法?在多线程中有哪些运用。
CAS,全称为CompareandSwap,即比较-替换。假定有三个操作数:内存值V、旧的预期值A、要修正的值B,当且仅当预期值A和内存值V相一起,才会将内存值修正为B并回来true,不然什么都不做并回来false。当然CAS必定要volatile变量配合,这样才干保证每次拿到的变量是主内存中最新的那个值,不然旧的预期值A对某条线程来说,永久是一个不会变的值A,只需某次CAS操作失利,永久都不或许成功。java.util.concurrent.atomic包下面的Atom****类都有CAS算法的运用。
30、怎样检测一个线程是否拥有锁?
java.lang.Thread#holdsLock办法
31、Jdk中排查多线程问题用什么指令?
jstack
32、线程同步需求留意什么?
1、尽量缩小同步的规模,增加体系吞吐量。
2、分布式同步锁无意义,要运用分布式锁。
3、防止死锁,留意加锁次序。
33、线程wait()办法运用有什么条件?
要在同步块中运用。
34、Fork/Join结构运用有哪些要留意的当地?
假如使命拆解的很深,体系内的线程数量堆积,导致体系功能功能严峻下降;
假如函数的调用栈很深,会导致栈内存溢出;
35、线程之间怎样传递数据?
通过在线程之间共享对象就可以了,然后通过wait/notify/notifyAll、await/signal/signalAll进行唤起和等候,比方说堵塞行列BlockingQueue便是为线程之间同享数据而规划的
36、保证”可见性”有哪几种办法?
synchronized和viotatile
37、说几个常用的Lock接口完结锁。
ReentrantLock、ReadWriteLock
38、ThreadLocal是什么?有什么运用场景?
ThreadLocal的作用是提供线程内的局部变量,这种变量在线程的生命周期内起作用,削减同一个线程内多个函数或许组件之间一些公共变量的传递的复杂度。用来处理数据库连接、Session管理等。
39、ReadWriteLock有什么用?
ReadWriteLock是一个读写锁接口,ReentrantReadWriteLock是ReadWriteLock接口的一个详细完结,完结了读写的别离,读锁是同享的,写锁是独占的,读和读之间不会互斥,读和写、写和读、写和写之间才会互斥,提升了读写的功能。
40、FutureTask是什么?
FutureTask表明一个异步运算的使命,FutureTask里边能够传入一个Callable的详细完结类,能够对这个异步运算的使命的成果进行等候获取、判别是否现已完结、取消使命等操作。
41、怎样唤醒一个堵塞的线程?
假如线程是因为调用了wait()、sleep()或许join()办法而导致的堵塞,能够中止线程,而且经过抛出InterruptedException来唤醒它;假如线程遇到了IO堵塞,无能为力,因为IO是操作体系完结的,Java代码并没有办法直接接触到操作体系。
42、不行变目标对多线程有什么协助?
不行变目标保证了目标的内存可见性,对不行变目标的读取不需求进行额定的同步手段,提
升了代码履行功率。
43、多线程上下文切换是什么意思?
多线程的上下文切换是指CPU操控权由一个现已正在运转的线程切换到另外一个就绪并等候获取CPU履行权的线程的进程。
44、Java顶用到了什么线程调度算法?
抢占式。一个线程用完CPU之后,操作体系会依据线程优先级、线程饥饿状况等数据算出一个总的优先级并分配下一个时刻片给某个线程履行。
45、Thread.sleep(0)的作用是什么?
因为Java采用抢占式的线程调度算法,因而或许会呈现某条线程常常获取到CPU操控权的状况,为了让某些优先级比较低的线程也能获取到CPU操控权,能够运用Thread.sleep(0)手动触发一次操作体系分配时刻片的操作,这也是平衡CPU操控权的一种操作。
46、什么是达观锁和失望锁?
达观锁:就像它的姓名一样,关于并发间操作发生的线程安全问题持达观状况,达观锁以为竞赛不总是会发作,因而它不需求持有锁,将比较-替换这两个动作作为一个原子操作尝试去修正内存中的变量,假如失利则表明发作冲突,那么就应该有相应的重试逻辑。
失望锁:仍是像它的姓名一样,关于并发间操作发生的线程安全问题持失望状况,失望锁以为竞赛总是会发作,因而每次对某资源进行操作时,都会持有一个独占的锁,就像synchronized,不管三七二十一,直接上了锁就操作资源了。
47、Hashtable的size()办法为什么要做同步?
同一时刻只能有一条线程履行固定类的同步办法,可是关于类的非同步办法,能够多条线程一起拜访。所以,这样就有问题了,或许线程A在履行Hashtable的put办法添加数据,线程B则能够正常调用size()办法读取Hashtable中当时元素的个数,那读取到的值或许不是最新的,或许线程A添加了完了数据,可是没有对size++,线程B就现已读取size了,那么关于线程B来说读取到的size必定是不精确的。而给size()办法加了同步之后,意味着线程B调用size()办法只需在线程A调用put办法完毕之后才干够调用,这样就保证了线程安全性CPU履行代码,履行的不是Java代码,这点很关键,必定得记住。Java代码最终是被翻译成机器码履行的,机器码才是实在能够和硬件电路交互的代码。即使你看到Java代码只需一行,乃至你看到Java代码编译之后生成的字节码也只需一行,也不意味着关于底层来说这句句子的操作只需一个。一句”returncount”假定被翻译成了三句汇编句子履行,一句汇编句子和其机器码做对应,完全或许履行完第一句,线程就切换了。
48、同步办法和同步块,哪种更好?
同步块,这意味着同步块之外的代码是异步履行的,这比同步整个办法更提升代码的功率。请知道一条原则:同步的规模越小越好。
49、什么是自旋锁?
自旋锁是采用让当时线程不停地的在循环体内履行完结的,当循环的条件被其他线程改动时
才干进入临界区。
50、Runnable和Thread用哪个好?
Java不支持类的多重承继,但答应你完结多个接口。所以假如你要承继其他类,也为了减
少类之间的耦合性,Runnable会更好。
51、Java中notify和notifyAll有什么差异?
notify()办法不能唤醒某个详细的线程,所以只需一个线程在等候的时分它才有用武之地。而notifyAll()唤醒一切线程并答应他们抢夺锁保证了至少有一个线程能持续运转。
52、为什么wait/notify/notifyAll这些办法不在thread类里边?
这是个规划相关的问题,它调查的是面试者对现有体系和一些普遍存在但看起来不合理的事物的观点。回答这些问题的时分,你要说明为什么把这些办法放在Object类里是有意义的,还有不把它放在Thread类里的原因。一个很明显的原因是JAVA提供的锁是目标级的而不是线程级的,每个目标都有锁,经过线程获得。假如线程需求等候某些锁那么调用目标中的wait()办法就有意义了。假如wait()办法界说在Thread类中,线程正在等候的是哪个锁就不明显了。简略的说,因为wait,notify和notifyAll都是锁级其他操作,所以把他们界说在Object类中因为锁归于目标。
53、为什么wait和notify办法要在同步块中调用?
主要是因为JavaAPI强制要求这样做,如果你不这么做,你的代码会抛出IllegalMonitorStateException反常。还有一个原因是为了防止wait和notify之间发生竞态条件。
54、为什么你应该在循环中检查等候条件?
处于等候状况的线程或许会收到错误警报和伪唤醒,假如不在循环中检查等候条件,程序就会在没有满意完毕条件的状况下退出。因而,当一个等候线程醒来时,不能以为它本来的等候状况仍然是有用的,在notify()办法调用之后和等候线程醒来之前这段时刻它或许会改动。这便是在循环中运用wait()办法作用更好的原因,你能够在Eclipse中创立模板调用wait和notify试一试。
55、Java中堆和栈有什么不同?
每个线程都有自己的栈内存,用于存储本地变量,办法参数和栈调用,一个线程中存储的变量对其它线程是不行见的。而堆是一切线程同享的一片共用内存区域。目标都在堆里创立,为了提升功率线程会从堆中弄一个缓存到自己的栈,假如多个线程运用该变量就或许引发问题,这时volatile变量就能够发挥作用了,它要求线程从主存中读取变量的值。
56、你怎样在Java中获取线程仓库?
关于不同的操作体系,有多种办法来获得Java进程的线程仓库。当你获取线程仓库时,JVM会把一切线程的状况存到日志文件或许输出到操控台。在Windows你能够运用Ctrl+Break组合键来获取线程仓库,Linux下用kill-3指令。你也能够用jstack这个东西来获取,它对线程id进行操作,你能够用jps这个东西找到id。
57、怎样创立线程安全的单例形式?
单例形式即一个JVM内存中只存在一个类的目标实例分类
1、懒汉式
类加载的时分就创立实例
2、饿汉式
运用的时分才创立实例
58、什么是堵塞式办法?
堵塞式办法是指程序会一向等候该办法完结期间不做其他工作,ServerSocket的accept()办法便是一向等候客户端连接。这儿的堵塞是指调用成果回来之前,当时线程会被挂起,直到得到成果之后才会回来。此外,还有异步和非堵塞式办法在使命完结前就回来。
59、提交使命时线程池行列已满会时发会生什么?
当线程数小于最大线程池数maximumPoolSize时就会创立新线程来处理,而线程数大于等于最大线程池数maximumPoolSize时就会履行拒绝策略。

未经允许不得转载:IT技术网站 » 2020年多线程面试题预备总结
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!

 

志在指尖 用双手敲打未来

登录/注册IT技术大全

热门IT技术

C#基础入门   SQL server数据库   系统SEO学习教程   WordPress小技巧   WordPress插件   脚本与源码下载