【深入理解JVM】第10~13章 编译期优化、线程安全、锁优化 笔记

来源:互联网 发布:女生身高多少最好知乎 编辑:程序博客网 时间:2024/06/05 02:09

打算把这4章整理到一篇博文中来讲,因为这几章内容都比较偏理论和底层,每章内容也不算很多,一般开发者接触也十分有限,这里主要挑那些比较有意义的来讲讲。

第10章

1、语法糖

所谓语法糖其实就是语言提供了一些小技巧来帮助开发者更快地编写代码,而这些技巧不会提供太多实质上的改进,而且如果过度依赖,会容易看不清其真正的面目。这里讲几个Java中常见的语法糖。

1、泛型与类型擦除。

Java的泛型是依靠擦除来实现的,这一点相信大家都有所了解,实质上这也是一种伪泛型,所以我们先看看泛型真正的面目是怎样的:
源代码:

        List<Integer> list1 = new ArrayList<>();        list1.add(1);        List<String> list2 = new ArrayList<>();        list2.add("a");

反编译:

        ArrayList list1 = new ArrayList();        list1.add(Integer.valueOf(1));        ArrayList list2 = new ArrayList();        list2.add("a");

看到区别的吧?类型完全不见了,于是灵机一动,来做个小测试:

        List<Main> s = new ArrayList<>();        List<String> b = new ArrayList<>();        System.out.println(s.equals(b));

输出结果是:true,因为你看看反编译的代码就知道了:

        ArrayList s = new ArrayList();        ArrayList b = new ArrayList();        System.out.println(s.equals(b));

书上还列举了泛型在重载上的一个鬼畜般的Bug,似乎发现了一种重载也依赖返回参数类型的情况。但我在自己的环境下试了一下,发现编译报错,说明官方还是弥补了这个漏洞,这里就不列举了。

2、自动装箱、拆箱与遍历循环

这个绝对有意思,刷新了我的三观,在说装箱语法糖之前,提一下foreach遍历循环语法糖,其实就是对于所有实现了Iterable
的对象,使用其iterator方法结合next进行遍历,而Collection接口继承于Iterable接口。好了下面来看自动装箱糖,看代码:

        Integer a = 1;        Integer b = 2;        Integer c = 3;        Integer d = 3;        Integer e = 321;        Integer f = 321;        Long g = 3L;        System.out.println(c == d);        System.out.println(e == f);        System.out.println(c == (a + b));        System.out.println(c.equals(a + b));        System.out.println(g == (a + b));        System.out.println(g.equals(a + b));

书上没有直接给答案,不试不知道,一试吓一跳,好吧,我承认我对Integer的认识还是不够,答案如下:

truefalsetruetruetruefalse

至于为何,嘿嘿,看Integer的构造器去吧。

3、条件编译

这个厉害了,之前在《安卓开发艺术探索》里看到过类似if(false)....的代码,感觉这是作嘛?结果才晓得原来这是条件编译。所谓条件编译就是在if语句中给定一个常量的条件,然后在编译器运行的时候,就会自动选择if体内的代码或者else内的代码进行编译,而非两边都编译。

4、插入式注解处理器

唔,我感觉自己还不需要接触这么深,需要的话看书就好,这里不总结了。

第11章

还没有看

第12章

Java内存模型

由于Java的初衷是在各个平台上运行,但问题是不同计算设备上计算机系统结构不一样,处理器个数、核数、高速缓存机制、主存机制都不尽相同,所以JVM就搞了自己的内存模型,以便在所有机器上都有一致的内存访问结果。于是这个模型样子如下:

每个线程对应有一个线程私有的工作内存,线程不能直接对主内存的数据进行修改,而必须读入到工作内存中才可以。很显然,原来听过的那些“线程安全”之类的词就从这里开始产生了。内存见有如下8种原子操作:

  1. lock 把主存的一个变量标记为线程独占
  2. unlock 与上面相反
  3. read 把主存的变量传输到工作内存中,以便之后的load使用。
  4. load 把read从主存中得到的值放入工作内存变量副本中。
  5. use 把一个工作内存的变量传给执行引擎。
  6. assign 把执行引擎的结果赋值给工作内存的变量。
  7. store 把工作内存的变量传输到主内存。
  8. write 把store传来的值写回到主内存。

有几点要注意下,read和load不一定连续,store和write也是。

volatile关键字

这个不多说了,在另一篇博文中有提到,传送门:http://blog.csdn.net/qq_22123283/article/details/70214461

线程的实现

有几个概念:内核线程,轻量级进程,用户线程,系统态,用户态。

  1. 1:1线程模型,一个线程也就是一个轻量级进程,对应着一个内核线程。而内核线程是有限的,且切换比较麻烦。
  2. 1:N模型,纯用户线程实现。难度比较大,Java后来放弃。
  3. N:M模型,M个用户线程对应了N个轻量级进程,N个轻量级系统进程对应着N个内核线程。由系统处理之间的协同。这是Java所真正采用的。

其他

Java线程优先级需要对应操作系统所支持的优先级,有的时候有些优先级是相同的。线程的5个状态:新建(new),运行(running),无限等待(需要被其他线程唤醒),有限等待(系统帮助唤醒),阻塞(比如遇到同步锁),结束。

第13章

主要有两个方面内容,一个是线程安全的分级,一个是JVM对锁的优化。

线程安全分级

  1. 不可变,如final定义的。
  2. 绝对线程安全。不需要做任何其他同步操作都不会产生问题的操作。Java中标注线程安全的类均不属于此类,大部分属于下一个类别。
  3. 相对线程安全。大部分情况下不会产生问题,特殊情况下需要用同步措施处理。如Vector,HashTable等。
  4. 线程兼容。需要在调用端添加同步代码才可以正确访问的。
  5. 线程对立。不可能同时访问的。

锁优化

这些内容可知可不知,只要晓得你写的那些代码,比如ReentrantLock和Synchronized等这些代码,尤其是后者,是会被JVM不断优化,能消除就消除,能提高效率就提高效率处理的就好了。懒得列了哈。

0 0
原创粉丝点击