勤学教育网合作机构>石家庄培训>

石家庄兄弟连教育

欢迎您!

全国统一学习专线 8:30-21:00
石家庄兄弟连教育

石家庄兄弟连分层次分阶段的学习,由浅入深,根据学员的不同层次因材施教,授课中使用的案例都是真实的案例

石家庄兄弟连教育> 石家庄教育培训>

石家庄java中级培训

  • 课程介绍

  • 相关知识

  • 2019-11-18
石家庄兄弟连教育

Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。Java语言作为静态面向对象编程语言的代表,极好地实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程。

石家庄兄弟连课程体系




Java语言特点
  • 简单性
    Java看起来设计得很像C++,但是为了使语言小和容易熟悉,设计者们把C++语言中许多可用的特征去掉了,这些特征是一般程序员很少使用的。
  • 分布性
    Java设计成支持在网络上应用,它是分布式语言。Java既支持各种层次的网络连接,又以Socket类支持可靠的流(stream)网络连接,所以用户可以产生分布式的客户机和服务器。
  • 稳健性
    Java原来是用作编写消费类家用电子产品软件的语言,所以它是被设计成写高可靠和稳健软件的。Java消除了某些编程错误,使得用它写可靠软件相当容易。
  • 可移植性
    Java使得语言声明不依赖于实现的方面。例如,Java显式说明每个基本数据类型的大小和它的运算行为(这些数据类型由Java语法描述)。Java环境本身对新的硬件平台和操作系统是可移植的。

大咖教学天团,课程体系引领IT革新
他们曾经敲过的代码现如今已是业界标杆,是中国互联网的参与者与见证者!

  • 马如忠 马如忠 icon

    精通Java、C、C++等主流开发语言,5年以上开发经验,非常丰富的IT从业经验和教育心得,兄弟连特色JAVA讲师,曾担任达内课程总监级讲师。

  • 李康 李康 icon

    曾任用友软件,飞企集团等上市公司技术总监,CTO等职务,12年以上的软件开发和项目管理经验,领导并参与研发中国移动ERP终端系统。


石家庄兄弟连除了强大的线下集中培训外,为在职和自学能力较强人士量身打造的IT云课堂,将线上课程与线下新的面授课同步,并通过一对一贴身服务等措施,实现了优质的教学效果。石家庄兄弟连还与国内200多所高等院校展开深度合作,让在校大学生参与真实的企业级项目研发,独立设计开发商业项目,享受到和石家庄兄弟连学员一样的企业级项目实战训练,广受各界好评。


体验才是王道
高起点才更牛叉
缔造Java课程行业新标准
 
 

详情请进入 石家庄兄弟连教育 已关注:89 咨询电话:

相关知识点:Java 在语言层次上实现了对线程的支持。它提供了Thread/Runnable/ThreadGroup等一系列封装的类和接口,让程序员可以高效的开发 Java多线程应用。为了实现同步,Java提供了synchronize关键字以及object的wait()/notify()机制,可是在简单易用 的背后,应藏着更为复杂的玄机,很多问题就是由此而起。

一、Java内存模型

在了解Java的同步秘密之前,先来看看JMM(Java Memory Model)。

Java被设计为跨平台的语言,在内存管理上,显然也要有一个统一的模型。而且Java语言最大的特点就是废除了指针,把程序员从痛苦中解脱出来,不用再考虑内存使用和管理方面的问题。

可惜世事总不尽如人意,虽然JMM设计上方便了程序员,但是它增加了虚拟机的复杂程度,而且还导致某些编程技巧在Java语言中失效。

JMM 主要是为了规定了线程和内存之间的一些关系。对Java程序员来说只需负责用synchronized同步关键字,其它诸如与线程/内存之间进行数据交换 /同步等繁琐工作均由虚拟机负责完成。如图1所示:根据JMM的设计,系统存在一个主内存(MainMemory),Java中所有变量都储存在主存中, 对于所有线程都是共享的。每条线程都有自己的工作内存(WorkingMemory),工作内存中保存的是主存中某些变量的拷贝,线程对所有变量的操作都 是在工作内存中进行,线程之间无法相互直接访问,变量传递均需要通过主存完成。

图1 Java内存模型示例图

线程若要 对某变量进行操作,必须经过一系列步骤:首先从主存复制/刷新数据到工作内存,然后执行代码,进行引用/赋值操作,最后把变量内容写回 MainMemory。Java语言规范(JLS)中对线程和主存互操作定义了6个行为,分别为load,save,read,write,assign 和use,这些操作行为具有原子性,且相互依赖,有明确的调用先后顺序。具体的描述请参见JLS第17章。

我们在前面的章节介绍了synchronized的作用,现在,从JMM的角度来重新审视synchronized关键字。

假设某条线程执行一个synchronized代码段,其间对某变量进行操作,JVM会依次执行如下动作:

(1) 获取同步对象monitor (lock)

(2) 从主存复制变量到当前工作内存 (read and load)

(3) 执行代码,改变共享变量值 (use and assign)

(4) 用工作内存数据刷新主存相关内容 (store and write)

(5) 释放同步对象锁 (unlock)

可 见,synchronized的另外一个作用是保证主存内容和线程的工作内存中的数据的一致性。如果没有使用synchronized关键字,JVM不保 证第2步和第4步会严格按照上述次序立即执行。因为根据JLS中的规定,线程的工作内存和主存之间的数据交换是松耦合的,什么时候需要刷新工作内存或者更 新主内存内容,可以由具体的虚拟机实现自行决定。如果多个线程同时执行一段未经synchronized保护的代码段,很有可能某条线程已经改动了变量的 值,但是其他线程却无法看到这个改动,依然在旧的变量值上进行运算,最终导致不可预料的运算结果。

二、DCL失效

这一节我们要讨论的是一个让Java丢脸的话题:DCL失效。在开始讨论之前,先介绍一下LazyLoad,这种技巧很常用,就是指一个类包含某个成员变量,在类初始化的时候并不立即为该变量初始化一个实例,而是等到真正要使用到该变量的时候才初始化之。

例如下面的代码:

代码1

class Foo {

private Resource res=null;

public Resource getResource() {

if (res==null)

res=new Resource();

return res;

}

}

由于LazyLoad可以有效的减少系统资源消耗,提高程序整体的性能,所以被广泛的使用,连Java的缺省类加载器也采用这种方法来加载Java类。

在 单线程环境下,一切都相安无事,但如果把上面的代码放到多线程环境下运行,那么就可能会出现问题。假设有2条线程,同时执行到了if(res==null),那么很有可能res被初始化2次,为了避免这样的RaceCondition,得用synchronized关键字把上面的方法同步起 来。代码如下:

代码2

Class Foo {

Private Resource res=null;

Public synchronized Resource getResource() {

If (res==null)

res=new Resource();

return res;

}

}

现在Race Condition解决了,一切都很好。

N 天过后,好学的你偶然看了一本Refactoring的魔书,深深为之打动,准备自己尝试这重构一些以前写过的程序,于是找到了上面这段代码。你已经不再 是以前的Java菜鸟,深知synchronized过的方法在速度上要比未同步的方法慢上100倍,同时你也发现,只有第一次调用该方法的时候才需要同 步,而一旦res初始化完成,同步完全没必要。所以你很快就把代码重构成了下面的样子:

代码3

Class Foo {

Private Resource res=null;

Public Resource getResource() {

If (res==null){

synchronized(this){

if(res==null){

res=new Resource();

}

}

}

return res;

}

}

这种看起来很完美的优化技巧就是Double-Checked Locking。但是很遗憾,根据Java的语言规范,上面的代码是不可靠的。

造 成DCL失效的原因之一是编译器的优化会调整代码的次序。只要是在单个线程情况下执行结果是正确的,就可以认为编译器这样的“自作主张的调整代码次序”的 行为是合法的。JLS在某些方面的规定比较自由,就是为了让JVM有更多余地进行代码优化以提高执行效率。而现在的CPU大多使用超流水线技术来加快代码 执行速度,针对这样的CPU,编译器采取的代码优化的方法之一就是在调整某些代码的次序,尽可能保证在程序执行的时候不要让CPU的指令流水线断流,从而 提高程序的执行速度。正是这样的代码调整会导致DCL的失效。为了进一步证明这个问题,引用一下《DCL BrokenDeclaration》文章中的例子:

设一行Java代码:

Objects[i].reference=new Object();

经过Symantec JIT编译器编译过以后,最终会变成如下汇编码在机器中执行:

0206106A mov eax,0F97E78h

0206106F call 01F6B210 ;为Object申请内存空间

; 返回值放在eax中

02061074 mov dword ptr [ebp],eax ; EBP 中是objects[i].reference的地址

; 将返回的空间地址放入其中

; 此时Object尚未初始化

02061077 mov ecx,dword ptr [eax] ; dereference eax所指向的内容

; 获得新创建对象的起始地址

02061079 mov dword ptr [ecx],100h ; 下面4行是内联的构造函数

0206107F mov dword ptr [ecx+4],200h

02061086 mov dword ptr [ecx+8],400h

0206108D mov dword ptr [ecx+0Ch],0F84030h

可见,Object构造函数尚未调用,但是已经能够通过objects[i].reference获得Object对象实例的引用。

如 果把代码放到多线程环境下运行,某线程在执行到该行代码的时候JVM或者操作系统进行了一次线程切换,其他线程显然会发现msg对象已经不为空,导致 Lazy load的判断语句if(objects[i].reference==null)不成立。线程认为对象已经建立成功,随之可能会使用对象的成员变量或者调用该对象实例的方法,最终导致不可预测的错误。

原 因之二是在共享内存的SMP机上,每个CPU有自己的Cache和寄存器,共享同一个系统内存。所以CPU可能会动态调整指令的执行次序,以更好的进行并 行运算并且把运算结果与主内存同步。这样的代码次序调整也可能导致DCL失效。回想一下前面对Java内存模型的介绍,我们这里可以把 MainMemory看作系统的物理内存,把Thread WorkingMemory认为是CPU内部的Cache和寄存器,没有synchronized的保护,Cache和寄存器的内容就不会及时和主内存的 内容同步,从而导致一条线程无法看到另一条线程对一些变量的改动。

结合代码3来举例说明,假设Resource类的实现如下:

Class Resource{

Object obj;

}

即Resource类有一个obj成员变量引用了Object的一个实例。假设2条线程在运行,其状态用如下简化图表示:

图2

现 在Thread-1构造了Resource实例,初始化过程中改动了obj的一些内容。退出同步代码段后,因为采取了同步机制,Thread-1所做的改 动都会反映到主存中。接下来Thread-2获得了新的Resource实例变量res,由于没有使用synchronized保护所以Thread-2 不会进行刷新工作内存的操作。假如之前Thread-2的工作内存中已经有了obj实例的一份拷贝,那么Thread-2在对obj执行use操作的时候 就不会去执行load操作,这样一来就无法看到Thread-1对obj的改变,这显然会导致错误的运算结果。此外,Thread-1在退出同步代码段的 时刻对ref和obj执行的写入主存的操作次序也是不确定的,所以即使Thread-2对obj执行了load操作,也有可能只读到obj的初试状态的数 据。(注:这里的load/use均指JMM定义的操作)

有很多人不死心,试图想出了很多精妙的办法来解决这个问题,但最终都失败了。 事实上,无论是目前的JMM还是已经作为JSR提交的JMM模型的增强,DCL都不能正常使用。在William Pugh的论文《Fixingthe Java MemoryModel》中详细的探讨了JMM的一些硬伤,更尝试给出一个新的内存模型,有兴趣深入研究的读者可以参见文后的参考资料。

如果你设计的对象在程序中只有一个实例,即singleton的,有一种可行的解决办法来实现其LazyLoad:就是利用类加载器的LazyLoad特性。代码如下:

Class ResSingleton {

public static Resource res=new Resource();

}

这里ResSingleton只有一个静态成员变量。当第一次使用ResSingleton.res的时候,JVM才会初始化一个Resource实例,并且JVM会保证初始化的结果及时写入主存,能让其他线程看到,这样就成功的实现了LazyLoad。

除了这个办法以外,还可以使用ThreadLocal来实现DCL的方法,但是由于ThreadLocal的实现效率比较低,所以这种解决办法会有较大的性能损失,有兴趣的读者可以参考文后的参考资料。

  • 校区地址
  • 学校相册
  • 热门课程
  • 同类机构
  • 其他课程

温馨提示:提交留言后老师会第一时间与您联系!热线电话:

手机访问

#tel_400#