仓酷云

标题: JAVA网页设计Java多线程开展简史仓酷云 [打印本页]

作者: 透明    时间: 2015-1-18 11:24
标题: JAVA网页设计Java多线程开展简史仓酷云
到时我们不用学struts,不用学spring,不用学Hibernate,只要能把jsf学会了,完全可以替代所有的框架,包括AJAX,都知道AJAX并不是新技术,虽说我没深入学习jsf但我认为jsf应该已经能通过其它技术替代AJAX,实现无缝刷新。这篇文章,年夜部份内容,是周五我做的一个关于怎样举行Java多线程编程的KnowledgeSharing的一个收拾,我但愿能对Java从第一个版本入手下手,在多线程编程方面的年夜事务和开展头绪有一个形貌,而且说起一些在多线程编程方面罕见的成绩。关于Java程序员来讲,假如从汗青的角度往懂得一门言语一个特征的演进,也许能有分歧劳绩。
弁言
起首问如许一个成绩,假如提到Java多线程编程,你会想到甚么?

好吧,请包涵我在这里卖的关子,实在这些都对,可是又不敷够周全,假如我们如许来议论Java多线程会不会周全一些:
但是,这不免太古板了,不是么?
不如换一个思绪,我们少谈一些很简单查到的语法,无妨从汗青的角度看看Java在多线程编程方面是如何退化的,这个过程当中,它做了哪些准确的决意,犯了哪些毛病,将来又会有如何的开展趋向?
别的,另有一点要说是,我但愿经由过程大批的实例代码来讲明这些事变。Linus说:“Talkischeap,showmethecode.”。下文触及到的代码我已上传,能够在此打包下载。
出生
Java的基因来自于1990年12月Sun公司的一个外部项目,方针设备恰是家用电器,可是C++的可移植性和API的易用性都让程序员恶感。旨在办理如许的成绩,因而又了Java的前身Oak言语,可是晓得1995年3月,它正式改名为Java,才算Java言语真实的出生。
JDK1.0
1996年1月的JDK1.0版本,从一入手下手就建立了Java最基本的线程模子,而且,如许的线程模子再后续的修修补补中,并未产生本色性的变动,能够说是一个具有传承性的优秀计划。
抢占式和合作式是两种罕见的历程/线程调剂体例,操纵体系十分合适利用抢占式体例来调剂它的历程,它给分歧的历程分派工夫片,关于临时无呼应的历程,它有才能褫夺它的资本,乃至将其强行中断(假如接纳合作式的体例,必要历程盲目、自动地开释资本,大概就不晓得必要比及甚么时分了)。Java言语一入手下手就接纳合作式的体例,而且在前面开展的过程当中,慢慢放弃失落了粗犷的stop/resume/suspend如许的办法,它们是违反合作式的不良计划,转而接纳wait/notify/sleep如许的双方线程共同举动的体例。
一种线程间的通讯体例是利用中止:
publicclassInterruptCheckextendsThread{
@Override
publicvoidrun(){
System.out.println("start");
while(true)
if(Thread.currentThread().isInterrupted())
break;
System.out.println("whileexit");
}
publicstaticvoidmain(String[]args){
Threadthread=newInterruptCheck();
thread.start();
try{
sleep(2000);
}catch(InterruptedExceptione){
}
thread.interrupt();
}
}

这是中止的一种利用体例,看起来就像是一个标记位,线程A设置这个标记位,线程B时不时地反省这个标记位。别的另有一种利用中止通讯的体例,以下:
publicclassInterruptWaitextendsThread{
publicstaticObjectlock=newObject();
@Override
publicvoidrun(){
System.out.println("start");
synchronized(lock){
try{
lock.wait();
}catch(InterruptedExceptione){
System.out.println(Thread.currentThread().isInterrupted());
Thread.currentThread().interrupt();//setinterruptflagagain
System.out.println(Thread.currentThread().isInterrupted());
e.printStackTrace();
}
}
}
publicstaticvoidmain(String[]args){
Threadthread=newInterruptWait();
thread.start();
try{
sleep(2000);
}catch(InterruptedExceptione){
}
thread.interrupt();
}
}

在这类体例下,假如利用wait办法处于守候中的线程,被另外一个线程利用中止叫醒,因而抛出InterruptedException,同时,中止标记扫除,这时候候我们一般会在捕捉该非常的中央从头设置中止,以便后续的逻辑经由过程反省中止形态来懂得该线程是怎样停止的。
在对照不乱的JDK1.0.2版本中,已能够找到Thread和ThreadUsage如许的类,这也是线程模子中最中心的两个类。全部版本只包括了如许几个包:java.io、java.util、java.net、java.awt和java.applet,以是说Java从一入手下手这个十分原始的版本就建立了一个耐久的线程模子。
值得一提的是,在这个版本中,原子对象AtomicityXXX已计划好了,这里给出一个例子,申明i++这类操纵时非原子的,而利用原子对象能够包管++操纵的原子性:
importjava.util.concurrent.atomic.AtomicInteger;
publicclassAtomicity{
privatestaticvolatileintnonAtomicCounter=0;
privatestaticvolatileAtomicIntegeratomicCounter=newAtomicInteger(0);
privatestaticinttimes=0;
publicstaticvoidcaculate(){
times++;
for(inti=0;i<1000;i++){
newThread(newRunnable(){
@Override
publicvoidrun(){
nonAtomicCounter++;
atomicCounter.incrementAndGet();
}
}).start();
}
try{
Thread.sleep(1000);
}catch(InterruptedExceptione){
}
}
publicstaticvoidmain(String[]args){
caculate();
while(nonAtomicCounter==1000){
nonAtomicCounter=0;
atomicCounter.set(0);
caculate();
}
System.out.println("Non-atomiccounter:"+times+":"
+nonAtomicCounter);
System.out.println("Atomiccounter:"+times+":"+atomicCounter);
}
}

下面这个例子你大概必要跑几回才干看到效果,利用非原子性的++操纵,了局常常小于1000。
关于锁的利用,网上能够找到各类申明,但表述都不敷明晰。请看上面的代码:
publicclassLock{
privatestaticObjecto=newObject();
staticLocklock=newLock();
//lockondynamicmethod
publicsynchronizedvoiddynamicMethod(){
System.out.println("dynamicmethod");
sleepSilently(2000);
}
//lockonstaticmethod
publicstaticsynchronizedvoidstaticMethod(){
System.out.println("staticmethod");
sleepSilently(2000);
}
//lockonthis
publicvoidthisBlock(){
synchronized(this){
System.out.println("thisblock");
sleepSilently(2000);
}
}
//lockonanobject
publicvoidobjectBlock(){
synchronized(o){
System.out.println("dynamicblock");
sleepSilently(2000);
}
}
//lockontheclass
publicstaticvoidclassBlock(){
synchronized(Lock.class){
System.out.println("staticblock");
sleepSilently(2000);
}
}
privatestaticvoidsleepSilently(longmillis){
try{
Thread.sleep(millis);
}catch(InterruptedExceptione){
e.printStackTrace();
}
}
publicstaticvoidmain(String[]args){
//objectlocktest
newThread(){
@Override
publicvoidrun(){
lock.dynamicMethod();
}
}.start();
newThread(){
@Override
publicvoidrun(){
lock.thisBlock();
}
}.start();
newThread(){
@Override
publicvoidrun(){
lock.objectBlock();
}
}.start();
sleepSilently(3000);
System.out.println();
//classlocktest
newThread(){
@Override
publicvoidrun(){
lock.staticMethod();
}
}.start();
newThread(){
@Override
publicvoidrun(){
lock.classBlock();
}
}.start();
}
}

下面的例子能够反应对一个锁合作的征象,分离下面的例子,了解上面这两条,就能够很简单了解synchronized关头字的利用:

JDK1.2
1998年岁尾的JDK1.2版本正式把Java分别为J2EE/J2SE/J2ME三个分歧偏向。在这个版本中,Java试图用Swing修改在AWT中犯的毛病,比方利用了太多的同步。惋惜的是,Java自己决意了AWT仍是Swing功能和呼应都难以使人中意,这也是Java桌面使用难以等到其服务端使用的一个缘故原由,在IBM厥后的SWT,也不敷以使人中意,JDK在这方面到JDK1.2后仿佛检查了本人,停下脚步了。值得注重的是,JDK高版本修复低版本成绩的时分,一般遵守如许的准绳:
在这个版本中,正式取销了如许三个办法:stop()、suspend()和resume()。上面我就来先容一下,为何它们要被取销:
publicclassStopextendsThread{
@Override
publicvoidrun(){
try{
while(true)
;
}catch(Throwablee){
e.printStackTrace();
}
}
publicstaticvoidmain(String[]args){
Threadthread=newStop();
thread.start();
try{
sleep(1000);
}catch(InterruptedExceptione){
}
thread.stop(newException("stop"));//notethestacktrace
}
}

从下面的代码你应当能够看出两件事变:
很难设想如许的计划出自一个连指针都被废失落的范例平安的编程言语,对不合错误?再来看看suspend的利用,有引发逝世锁的隐患:
publicclassSuspendextendsThread{
@Override
publicvoidrun(){
synchronized(this){
while(true)
;
}
}
publicstaticvoidmain(String[]args){
Threadthread=newSuspend();
thread.start();
try{
sleep(1000);
}catch(InterruptedExceptione){
}
thread.suspend();
synchronized(thread){//deadlock
System.out.println("gotthelock");
thread.resume();
}
}
}

从下面的代码能够看出,Suspend线程被挂起时,仍然占据锁,而当main线程希冀往猎取该线程来叫醒它时,完全瘫痪了。因为suspend在这里是无刻日限定的,这会酿成一个彻完全底的逝世锁。
相反,看看这三个办法的改善品和替换品:wait()、notify()和sleep(),它们令线程之间的交互就友爱很多:
publicclassWaitextendsThread{
@Override
publicvoidrun(){
System.out.println("start");
synchronized(this){//wait/notify/notifyAllusethesame
//synchronizationresource
try{
this.wait();
}catch(InterruptedExceptione){
e.printStackTrace();//notifywontthrowexception
}
}
}
publicstaticvoidmain(String[]args){
Threadthread=newWait();
thread.start();
try{
sleep(2000);
}catch(InterruptedExceptione){
}
synchronized(thread){
System.out.println("Wait()willreleasethelock!");
thread.notify();
}
}
}

在wait和notify搭配利用的过程当中,注重必要把它们锁定到统一个资本上(比方对象a),即:
再来看一看sleep办法的利用,回覆上面两个成绩:
假如我后面说的你都了解了,你应当能回覆这两个成绩。
publicclassSleepextendsThread{
@Override
publicvoidrun(){
System.out.println("start");
synchronized(this){//sleep()canuse(ornot)anysynchronizationresource
try{
/**
*Doyouknow:<br>
*1.Whysleep()isdesignedasastaticmethodcomparingwith
*wait?<br>
*2.Whysleep()musthaveatimeoutparameter?
*/
this.sleep(10000);
}catch(InterruptedExceptione){
e.printStackTrace();//notifywontthrowexception
}
}
}
publicstaticvoidmain(String[]args){
Threadthread=newSleep();
thread.start();
try{
sleep(2000);
}catch(InterruptedExceptione){
}
synchronized(thread){
System.out.println("Hassleep()releasedthelock!");
thread.notify();
}
}
}

在这个JDK版本中,引进线程变量ThreadLocal这个类:
JAVA网页设计Java多线程开展简史仓酷云
登录/注册后可看大图

<br>
每个线程都挂载了一个ThreadLocalMap。ThreadLocal这个类的利用很成心思,get办法没有key传进,缘故原由就在于这个key就是以后你利用的这个ThreadLocal它本人。ThreadLocal的对象性命周期能够陪伴着全部线程的性命周期。因而,倘使在线程变量里寄存延续增加的对象(最多见是一个不受优秀办理的map),很简单招致内存保守。
publicclassThreadLocalUsageextendsThread{
publicUseruser=newUser();
publicUsergetUser(){
returnuser;
}
@Override
publicvoidrun(){
this.user.set("var1");
while(true){
try{
sleep(1000);
}catch(InterruptedExceptione){
}
System.out.println(this.user.get());
}
}
publicstaticvoidmain(String[]args){
ThreadLocalUsagethread=newThreadLocalUsage();
thread.start();
try{
sleep(4000);
}catch(InterruptedExceptione){
}
thread.user.set("var2");
}
}
classUser{
privatestaticThreadLocal<Object>enclosure=newThreadLocal<Object>();//isitmustbestatic?
publicvoidset(Objectobject){
enclosure.set(object);
}
publicObjectget(){
returnenclosure.get();
}
}

下面的例子会一向打印var1,而不会打印var2,就是由于分歧线程中的ThreadLocal是相互自力的。
用jstack工具能够找到锁相干的信息,假如线程占据锁,可是因为实行到wait办法时处于wait形态临时开释了锁,会打印waitingon的信息:
"Thread-0"prio=6tid=0x02bc4400nid=0xef44inObject.wait()[0x02f0f000]
java.lang.Thread.State:WAITING(onobjectmonitor)
atjava.lang.Object.wait(NativeMethod)
-waitingon<0x22a7c3b8>(aWait)
atjava.lang.Object.wait(Object.java:485)
atWait.run(Wait.java:8)
-locked<0x22a7c3b8>(aWait)

假如程序延续占据某个锁(比方sleep办法在sleep时代不会开释锁),会打印locked的信息:
"Thread-0"prio=6tid=0x02baa800nid=0x1ea4waitingoncondition[0x02f0f000]
java.lang.Thread.State:TIMED_WAITING(sleeping)
atjava.lang.Thread.sleep(NativeMethod)
atWait.run(Wait.java:8)
-locked<0x22a7c398>(aWait)

而假如是线程但愿进进某同步块,而在守候锁的开释,会打印waitingto的信息:
"main"prio=6tid=0x00847400nid=0xf984waitingformonitorentry[0x0092f000]
java.lang.Thread.State:BLOCKED(onobjectmonitor)
atWait.main(Wait.java:23)
-waitingtolock<0x22a7c398>(aWait)

JDK1.4
在2002年4月公布的JDK1.4中,正式引进了NIO。JDK在原有尺度IO的基本上,供应了一组多路复用IO的办理计划。
经由过程在一个Selector上挂接多个Channel,经由过程一致的轮询线程检测,每当无数据抵达,触发监听事务,将事务分收回往,而不是让每个channel临时损耗堵塞一个线程守候数据流抵达。以是,只要在对资本争取激烈的高并发场景下,才干见到NIO的分明上风。
JAVA网页设计Java多线程开展简史仓酷云
登录/注册后可看大图

<br>
相较于面向流的传统体例这类面向块的会见体例会丧失一些浅易性和天真性。上面给出一个NIO接口读取文件的复杂例子(仅表示用):
importjava.io.FileInputStream;
importjava.io.IOException;
importjava.nio.ByteBuffer;
importjava.nio.channels.FileChannel;
publicclassNIO{
publicstaticvoidnioRead(Stringfile)throwsIOException{
FileInputStreamin=newFileInputStream(file);
FileChannelchannel=in.getChannel();
ByteBufferbuffer=ByteBuffer.allocate(1024);
channel.read(buffer);
byte[]b=buffer.array();
System.out.println(newString(b));
channel.close();
}
}

JDK5.0
2004年9月起JDK1.5公布,并正式改名到5.0。有个笑话说,软件行业有句话,叫做“不要用3.0版本以下的软件”,意义是说版本太小的话常常软件质量不外关——可是依照这类说法,JDK的原有版本定名体例得要到啥时分才有3.0啊,因而1.4今后经由过程版本定名体例的改动间接升到5.0了。
JDK5.0不但是版本号定名体例变动那末复杂,关于多线程编程来讲,这里产生了两个严重事务,JSR133和JSR166的正式公布。
JSR133
JSR133从头明白了Java内存模子,现实上,在这之前,罕见的内存模子包含一连分歧性内存模子和先行产生模子。
关于一连分歧性模子来讲,程序实行的按次和代码上显现的按次是完整分歧的。这关于古代多核,而且指令实行优化的CPU来讲,是很难包管的。并且,按次分歧性的包管将JVM对代码的运转期优化严峻限定住了。
可是JSR133指定的先行产生(Happens-before)使得实行指令的按次变得天真:

而在内存分派上,将每一个线程各自的事情内存(乃至包含)从主存中自力出来,更是给JVM大批的空间来优化线程内指令的实行。主存中的变量能够被拷贝到线程的事情内存中往独自实行,在实行停止后,了局能够在某个工夫刷回主存:
JAVA网页设计Java多线程开展简史仓酷云
登录/注册后可看大图

<br>
可是,如何来包管各个线程之间数据的分歧性?JLS给的举措就是,默许情形下,不克不及包管恣意时候的数据分歧性,可是经由过程对synchronized、volatile和final这几个语义被加强的关头字的利用,能够做到数据分歧性。要注释这个成绩,不如看一看典范的DCL(DoubleCheckLock)成绩:
publicclassDoubleCheckLock{
privatevolatilestaticDoubleCheckLockinstance;//DoIneedadd"volatile"here?
privatefinalElementelement=newElement();//ShouldIadd"final"here?Isa"final"enoughhere?OrIshoulduse"volatile"?
privateDoubleCheckLock(){
}
publicstaticDoubleCheckLockgetInstance(){
if(null==instance)
synchronized(DoubleCheckLock.class){
if(null==instance)
instance=newDoubleCheckLock();
//thewriteswhichinitializeinstanceandthewritetotheinstancefieldcanbereorderedwithout"volatile"
}
returninstance;
}
publicElementgetElement(){
returnelement;
}
}
classElement{
publicStringname=newString("abc");
}

在下面这个例子中,假如不合错误instance声明的中央利用volatile关头字,JVM将不克不及包管getInstance办法猎取到的instance是一个完全的、准确的instance,而volatile关头字包管了instance的可见性,即可以包管猎取到事先实在的instance对象。
可是成绩没有那末复杂,关于上例中的element而言,假如没有volatile和final润色,element里的name也没法在前文所述的instance前往给内部时的可见性。假如element是不成变对象,利用final也能够包管它在机关办法挪用后的可见性。
关于volatile的效果,良多人都但愿有一段冗长的代码可以看到,利用volatile和不利用volatile的情形下实行了局的不同。惋惜这实在其实不好找。这里我给出如许一个不甚严厉的例子:
publicclassVolatile{
publicstaticvoidmain(String[]args){
finalVolatilevolObj=newVolatile();
Threadt2=newThread(){
publicvoidrun(){
while(true){
volObj.check();
}
}
};
t2.start();
Threadt1=newThread(){
publicvoidrun(){
while(true){
volObj.swap();
}
}
};
t1.start();
}
booleanboolValue;//usevolatiletoprint"WTF!"
publicvoidcheck(){
if(boolValue==!boolValue)
System.out.println("WTF!");
}
publicvoidswap(){
try{
Thread.sleep(100);
}catch(InterruptedExceptione){
e.printStackTrace();
}
boolValue=!boolValue;
}
}

代码中存在两个线程,一个线程经由过程一个逝世轮回不休在变更boolValue的取值;另外一个线程每100毫秒实行“boolValue==!boolValue”,这行代码会取两次boolValue,能够设想的是,有必定几率会呈现这两次取boolValue了局纷歧致的情形,那末这个时分就会打印“WTF!”。
可是,下面的情形是对boolValue利用volatile润色包管其可见性的情形下呈现的,假如不合错误boolValue利用volatile润色,运转时就一次不会呈现(最少在我的电脑上)打印“WTF!”的情况,换句话说,这反而是不太一般的,我没法推测JVM做了甚么操纵,基础上独一能够断定的是,没有效volatile润色的时分,boolValue在猎取的时分,其实不能总取到最实在的值。
JSR166
JSR166的奉献就是引进了java.util.concurrent这个包。后面已经解说过AtomicXXX类这类原子范例,外部完成包管其原子性的实际上是经由过程一个compareAndSet(x,y)办法(CAS),而这个办法追踪到最底层,是经由过程CPU的一个独自的指令来完成的。这个办法所做的事变,就是包管在某变量取值为x的情形下,将取值x交换为y。在这个过程当中,并没有任何加锁的举动,以是一样平常它的功能要比利用synchronized高。
Lock-free算法就是基于CAS来完成原子化“set”的体例,一般有如许两种情势:
importjava.util.concurrent.atomic.AtomicInteger;
publicclassLockFree{
privateAtomicIntegermax=newAtomicInteger();
//typeA
publicvoidsetA(intvalue){
while(true){//1.circulation
intcurrentValue=max.get();
if(value>currentValue){
if(max.compareAndSet(currentValue,value))//2.CAS
break;//3.exit
}else
break;
}
}
//typeB
publicvoidsetB(intvalue){
intcurrentValue;
do{//1.circulation
currentValue=max.get();
if(value<=currentValue)
break;//3.exit
}while(!max.compareAndSet(currentValue,value));//2.CAS
}
}

不外,对CAS的利用其实不老是准确的,好比ABA成绩。我用上面如许一个栈的例子来讲明:
JAVA网页设计Java多线程开展简史仓酷云
登录/注册后可看大图

<br>
为何会产生如许的毛病?由于对t1来讲,它两次都检察到栈顶的A,觉得时代没有产生变更,而实践上呢?实践上已产生了变更,C出去、B进来了,可是t1它只看栈顶是A,它其实不晓得已经产生了甚么。
那末,有甚么举措能够办理这个成绩呢?
最多见的举措是利用一个计数器,对这个栈只需有任何的变更,就触发计数器+1,t1在要检察A的形态,不如看一下计数器的情形,假如计数器没有变更,申明时代没有他人动过这个栈。JDK5.0内里供应的AtomicStampedReference就是起这个用的。
利用immutable对象的拷贝(好比CopyOnWrite)也能够完成无锁形态下的并发会见。举一个复杂的例子,好比有如许一个链表,每个节点包括两个值,如今我要把两头一个节点(2,3)交换成(4,5),不利用同步的话,我能够如许完成:
JAVA网页设计Java多线程开展简史仓酷云
登录/注册后可看大图

<br>
构建一个新的节点连到节点(4,6)上,再将原有(1,1)到(2,3)的指针指向交换成(1,1)到(4,5)的指向。
除这二者,另有良多不必同步来完成原子操纵的办法,好比我已经先容过的Peterson算法。
以下这个表格显现了JDK5.0触及到的经常使用容器:
JAVA网页设计Java多线程开展简史仓酷云
登录/注册后可看大图

<br>
个中:

再说一下关于线程池的撑持。在说线程池之前,得明白一下Future的观点。Future也是JDK5.0新增的类,是一个用来整条约步和异步的了局对象。一个异步义务的实行经由过程Future对象当即前往,假如你希冀以同步体例猎取了局,只必要挪用它的get办法,直到了局获得才会前往给你,不然线程会一向hang在那边。Future能够看作是JDK为了它的线程模子做的一个部分修复,由于程序员以往在思索多线程的时分,其实不可以以面向对象的思绪往完成它,而不能不思索良多面向线程的举动,可是Future和前面要讲到的Barrier等类,可让这些特定情形下,程序员能够从沉重的线程头脑中摆脱出来。把线程把持的部分和营业逻辑的部分化耦开。
importjava.util.concurrent.Callable;
importjava.util.concurrent.ExecutionException;
importjava.util.concurrent.ExecutorService;
importjava.util.concurrent.Executors;
importjava.util.concurrent.Future;
publicclassFutureUsage{
publicstaticvoidmain(String[]args){
ExecutorServiceexecutor=Executors.newSingleThreadExecutor();
Callable<Object>task=newCallable<Object>(){
publicObjectcall()throwsException{
Thread.sleep(4000);
Objectresult="finished";
returnresult;
}
};
Future<Object>future=executor.submit(task);
System.out.println("tasksubmitted");
try{
System.out.println(future.get());
}catch(InterruptedExceptione){
}catch(ExecutionExceptione){
}
//Threadwontbedestroyed.
}
}

下面的代码是一个最复杂的线程池利用的例子,线程池承受提交下去的义务,分派给池中的线程往实行。关于义务压力的情形,JDK中一个功效完整的线程池具有如许的优先级处置战略:
关于年夜于coreSize而小于maxSize的那些线程,余暇了keepAliveTime后,会被烧毁。察看下面说的优先级按次能够看到,假设说给ExecutorService一个无穷长的行列,好比LinkedBlockingQueue,那末maxSize>coreSize就是没成心义的。
JAVA网页设计Java多线程开展简史仓酷云
登录/注册后可看大图

<br>
JDK6.0
JDK6.0对锁做了一些优化,好比锁自旋、锁打消、锁兼并、轻量级锁、所倾向等。在这里纷歧一先容,可是给一个例子以有理性熟悉:
<p>importjava.util.Vector;
<p>publicclassL
IDE是好。java中的IDE更是百花齐放,你用jbuilder能说jbuilder赶不上vs吗?用eclipse,net网页编程beans也很舒服啊。我就不明白“稍微差一些”那一些是从哪里差来的。
作者: 深爱那片海    时间: 2015-1-21 05:32
Java 编程语言的风格十分接近C、C++语言。
作者: 简单生活    时间: 2015-1-24 09:50
是一种由美国SUN计算机公司(Sun Microsystems, Inc.)所研究而成的语言
作者: 第二个灵魂    时间: 2015-1-31 22:54
你一定会高兴地说,哈哈,原来成为Java高手就这么简单啊!记得Tomjava也曾碰到过一个项目经理,号称Java很简单,只要三个月就可以学会。
作者: admin    时间: 2015-2-1 10:30
J2SE开发桌面应用软件比起 VC,VB,DEPHI这些传统开发语言来说,优势好象并不明显。J2ME对于初学者来说,好象又有点深奥,而且一般开发者很难有开发环境。
作者: 山那边是海    时间: 2015-2-6 18:20
不过,每次的执行编译后的字节码需要消耗一定的时间,这同时也在一定程度上降低了 Java 程序的运行效率。
作者: 只想知道    时间: 2015-2-18 00:09
应用在电视机、电话、闹钟、烤面包机等家用电器的控制和通信。由于这些智能化家电的市场需求没有预期的高,Sun公司放弃了该项计划。随着1990年代互联网的发展
作者: 若相依    时间: 2015-3-5 23:50
象、泛型编程的特性,广泛应用于企业级Web应用开发和移动应用开发。
作者: 精灵巫婆    时间: 2015-3-9 01:25
Pet Store.(宠物店)是SUN公司为了演示其J2EE编程规范而推出的开放源码的程序,应该很具有权威性,想学J2EE和EJB的朋友不要 错过了。
作者: 活着的死人    时间: 2015-3-11 20:32
多重继承(以接口取代)等特性,增加了垃圾回收器功能用于回收不再被引用的对象所占据的内存空间,使得程序员不用再为内存管理而担忧。在 Java 1.5 版本中,Java 又引入了泛型编程(Generic Programming)、类型安全的枚举、不定长参数和自动装/拆箱等语言特性。
作者: 谁可相欹    时间: 2015-3-13 10:07
是一种由美国SUN计算机公司(Sun Microsystems, Inc.)所研究而成的语言
作者: 仓酷云    时间: 2015-3-20 19:10
至于JDBC,就不用我多说了,你如果用java编过存取数据库的程序,就应该很熟悉。还有,如果你要用Java编发送电子邮件的程序,你就得看看Javamail 了。




欢迎光临 仓酷云 (http://ckuyun.com/) Powered by Discuz! X3.2