java lang

一些工具:
jbe-0.1.1
jca457.jar

关键字

  1. 编译
编译或运行都加上-verbose
javac -verbose ClassA.java
编译的时候,如果没有指定-classpath,(逐层向上找仍没找到)那么就会在import里找源码并编译成字节码
ClassA.java(fisrtpackage) <=> ClassB.java(firstpackage.secondpackage)
javac -verbose ClassA.java会自动在各自目录生成ClassB.class和ClassA.class
  1. import
  2. package
避免同名类,主要对class文件起作用(只是有些命令利用源文件package信息)。主要被java命令使用,java命令会检查执行路径(执行名,如target.test11.Haha)是否与包路径(真实记录名test11/Haha——姓+名)一致,如果不一致就会报错
注:javac编译时除非显式指定编译路径(-d target,如果指定了路径就编译到target/package指定路径),都会直接给你编译到.java文件所在的当前目录(即默认情况下),IDE编译到target目录是“指定target目录/package路径”
  1. volatile
内存屏障与JVM并发
修饰的变量不会从内存读入缓存,牺牲了性能但是换来了并发;修饰的变量之前和之后的变量的操作在被硬件优化(指令重排)后仍在volatile修饰的变量之前或之后,如果不用volatile则可能出现后面的变量被先操作
-
有volatile变量修饰的共享变量进行写操作的时候会多第二行汇编代码,通过查IA-32架构软件开发者手册可知,lock前缀的指令在多核处理器下会引发了两件事情。
    • 将当前处理器缓存行的数据会写回到系统内存。
    • 这个写回内存的操作会引起在其他CPU里缓存了该内存地址的数据无效。
处理器为了提高处理速度,不直接和内存进行通讯,而是先将系统内存的数据读到内部缓存(L1,L2或其他)后再进行操作,但操作完之后不知道何时会写到内存,如果对声明了Volatile变量进行写操作,JVM就会向处理器发送一条Lock前缀的指令,将这个变量所在缓存行的数据写回到系统内存。但是就算写回到内存,如果其他处理器缓存的值还是旧的,再执行计算操作就会有问题,所以在多处理器下,为了保证各个处理器的缓存是一致的,就会实现缓存一致性协议,每个处理器通过嗅探在总线上传播的数据来检查自己缓存的值是不是过期了,当处理器发现自己缓存行对应的内存地址被修改,就会将当前处理器的缓存行设置成无效状态,当处理器要对这个数据进行修改操作的时候,会强制重新从系统内存里把数据读到处理器缓存里。
这两件事情在IA-32软件开发者架构手册的第三册的多处理器管理章节(第八章)中有详细阐述。
java中volatile关键字的含义
来自 
Java 理论与实践: 正确使用 Volatile 变量
http://www.ibm.com/developerworks/cn/java/j-jtp06197.html
>
    ++x 实际上是三种操作(读、添加、存储)的简单组合
    使用volatile条件(
    对变量的写操作不依赖于当前值。
    该变量没有包含在具有其他变量的不变式中。
    )

Generics

一些测试:

    /*List paramList = new ArrayList<>();
        paramList.add(11);
        paramList.add(12);
        testGenerics(paramList);*/
    // 参数读,测上界
    public static void testGenerics(List paramList) {
        // 定义vs赋值
        // List srcList = new ArrayList();
        // 既要读也要写
        List srcList = new ArrayList();
        // next line can not add
        // extends上界 - 参数定义的上界,实际值的下界(取值并赋值时目标变量参数必须是继承链往上,或者强转)
        // List srcList = new ArrayList();
        srcList.add(1);
        srcList.add(2);
        srcList.add(2.9);
        // super下界 - 参数定义的下界,实际值的上界(对添加进来的值 - 继承链往下都能add,往上no)
        // srcList.add(new Object());
        List destList = new ArrayList<>(); // or "? super Number"
        destList.add(3);
        destList.add(4);
        destList.add(5.1);
        Collections.copy(destList, srcList);
        System.out.println("success");
        // 上界
        Object value = paramList.get(0);
    }
 
 

Java中的逆变与协变

协变、逆变、不变
PECS
Collections.copy
    public static  void copy(List dest, List src)
定义与赋值(形参、实参)

为了安全

extends上界
    定义后具体赋值为哪个(List或List)不知道,不能随便add,只能读(肯定是子类)
super下界
    ? super X定义后,赋值为X的父类(如List),具体为哪个父类不知道(该方法不知道,调用者知道;该方法内不能随便get,有没有具体类的方法不知道),但是add X的子类肯定没问题
 
 

一些约定

1.问号"?"
https://stackoverflow.com/questions/678822/what-is-the-difference-between-and-object-in-java-generics
HashMap hash1;
is equivalent to
HashMap hash1;
Couple this knowledge with the "Get and Put Principle" in section (2.4) from Java Generics and Collections:

The Get and Put Principle: use an extends wildcard when you only get values out of a structure, use super wildcard when you only put values into a structure, and don't use a wildcard when you both get and put.

and the wild card may start making more sense

2.

reference

WeakHashMap
ThreadLocal dereference/purge
CloseableThreadLocal

多线程

Dekker算法
线程调度

乐观悲观有内涵概念和外延概念两类
我先说内涵概念
cpu是时分复用的
也就是把cpu切片,一片给一个thread/process
片与片之间,需要进行cpu切换
切换涉及到清空寄存器,缓存数据。然后重新加载新的thread所需数据
这个操作必然的会有一定代价
这个一般叫状态切换,碰到需要等待的,就切换出去,到blocking队列,然后等条件具备了,在通过notify()。notifyAll()唤醒回来
那么,悲观锁,其实就是要经过这个过程的锁
在某个资源不可用的时候,就将cpu让出,把当前等待线程切换为blocking状态
等到资源(比如一个共享数据)可用了,那么就将线程唤醒,让他进入runnable状态
等待cpu调度
那么 可以看到,如果一个资源占用时间很短,那么你第一次看,可能这个资源被占用,但当你花费100000个周期切换到blocking的时候,可能立刻就发现自己被唤醒了,于是再花费100000个周期回来抢锁
这个代价明显就高了
于是就产生了乐观锁的概念,他的核心思路就是,我不让出cpu,我while循环1000次,每次2个时钟周期,那么总共我花费了2000个时钟周期,可能就能拿到这个资源
#概数核心是比,你们要分清主次矛盾#
于是,当数据争用不严重,或者入锁处理出锁比较快的时候,乐观锁往往会有更好地效果

内涵概念讲解完毕,再看外延概念

可能由于网络原因,"可以看到,乐观锁的核心是不让出,通过while(true)循环的方式来判断当前锁状况,于是就引申一下, 在各类分布式系统中,“判断某个数据是否能访问,不能访问立刻返回false",通过循环来获得某个资源的访问权限的过程,都被叫做乐观锁了"的消息发送失败。
可能由于网络原因,"对应到Java实现,那么可以认为 tryLock接口是乐观锁实现中的原子接口"的消息发送失败。
有这个外延的原因是,他们实现的方式都一样,类似
while(true)
{
tryLock() == true?
{
       do sth.
}
}

Java内存模型

volatile(轻量级synchronized?) 内存屏障 happen-before(不保证原子操作)
本地内存(寄存器)
二级缓存 - 主存
1.读取、修改、写入
2.本地 内存
3.原子操作与i++操作
原子性
Atomic  compare and swap
    AtomicInteger.getAndDecrement
    (Unsafe.getAndAddInt(getIntVolatile/compareAndSwapInt))
4.缓存一致性协议
5.锁(互斥性/可见性)
synchronized(原子(互斥?)/可见)
volatile 可见性(no原子性)
    counter(volatile + synchronized)

你可能感兴趣的:(java lang)