最近有很多数据运算方面的需求,有很多对象之间运算,BigDecimal只能用方法进行运算,当有多个计算时候阅读很不方便.
考虑是否有类似 C++和运算符重载方法,但是 Java 本身不支持运算符重载,在查找资料中发现了一个工具manifold,在编译前加插件来实现运算符重载的功能,相当是一种语法糖,让对象之间可以使用运算符
当然manifold还提供了一些其他强大的扩展功能,但是不建议在正式的项目中使用,做一些试验的功能还是可以的,减少编码量,快速验证一些功能
最近有很多数据运算方面的需求,有很多对象之间运算,BigDecimal只能用方法进行运算,当有多个计算时候阅读很不方便.
考虑是否有类似 C++和运算符重载方法,但是 Java 本身不支持运算符重载,在查找资料中发现了一个工具manifold,在编译前加插件来实现运算符重载的功能,相当是一种语法糖,让对象之间可以使用运算符
当然manifold还提供了一些其他强大的扩展功能,但是不建议在正式的项目中使用,做一些试验的功能还是可以的,减少编码量,快速验证一些功能
JAVA maven:https://mvnrepository.com
Python pip: https://pypi.org
PHP composer:https://packagist.org/
.NET NuGet:https://www.nuget.org/
Conan, the C/C++ Package Manager
C/C++ conan:https://conan.io/
NODEJS npm:https://www.npmjs.com/
RUST cargo:https://crates.io
有时候在执行pip更新失败后,再次执行pip命令时会提示ModuleNotFoundError: No module named ‘pip’错误,导致pip命令无法使用。
重新打开一个cmd命令窗口,选择使用管理员权限打开:可以直接右键或是点击右侧功能,以管理员身份运行;
当在执行pip更新时,出现失败或警告,再次执行pip命令报错时,分别按顺序执行以下2条命令即可完成修复。
python -m ensurepip
python -m pip install –upgrade pip
Synchronized是基于monitor实现的,Synchronized经过编译后,会在同步块前后分别形成monitorenter和monitorexit两个字节码指令,在执行monitorenter指令时,首先要尝试获取对象锁,如果对象没有别锁定,或者当前已经拥有这个对象锁,把锁的计数器加1,相应的在执行monitorexit指令时,会将计数器减1,当计数器为0时,锁就被释放了。
如果获取锁失败,那当前线程就要阻塞,直到对象锁被另一个线程释放为止。
Java 虚拟机中的同步(Synchronization)基于进入和退出管程(Monitor)对象实现, 无论是显式同步(有明确的 monitorenter 和 monitorexit 指令,即同步代码块)还是隐式同步都是如此。在 Java 语言中,同步用的最多的地方可能是被 synchronized 修饰的同步方法。
同步方法 并不是由 monitorenter 和 monitorexit 指令来实现同步的,而是由方法调用指令读取运行时常量池中方法的 ACC_SYNCHRONIZED 标志来隐式实现的,关于这点,稍后详细分析。
下面先来了解一个概念Java对象头,这对深入理解synchronized实现原理非常关键。
synchronized特点:可重入锁/排他锁/属于jvm,由jvm实现
可重入锁实现可重入性原理或机制是:
每一个锁关联一个线程持有者和计数器,当计数器为 0 时表示该锁没有被任何线程持有,那么任何线程都可能获得该锁而调用相应的方法;
当某一线程请求成功后,JVM会记下锁的持有线程,并且将计数器置为 1;此时其它线程请求该锁,则必须等待;而该持有锁的线程如果再次请求这个锁,就可以再次拿到这个锁,同时计数器会递增;当线程退出同步代码块时,计数器会递减,如果计数器为 0,则释放该锁。
synchronized保证可见性
JMM关于synchronized的两条规定:
1)线程解锁前,必须把共享变量的最新值刷新到主内存中
2)线程加锁时,将清空工作内存中共享变量的值,从而使用共享变量时需要从主内存中重新获取最新的值
锁的状态总共有四种,无锁状态、偏向锁、轻量级锁和重量级锁。随着锁的竞争,锁可以从偏向锁升级到轻量级锁,再升级的重量级锁,但是锁的升级是单向的,也就是说只能从低到高升级,不会出现锁的降级。
在JVM中,对象在内存中的布局分为三块区域:对象头、实例数据和对齐填充。
实例变量:存放类的属性数据信息,包括父类的属性信息,如果是数组的实例部分还包括数组的长度,这部分内存按4字节对齐。
填充数据:由于虚拟机要求对象起始地址必须是8字节的整数倍。填充数据不是必须存在的,仅仅是为了字节对齐,这点了解即可。
Java头对象,它实现synchronized的锁对象的基础,synchronized使用的锁对象是存储在Java对象头里的,jvm中采用2个字来存储对象头(如果对象是数组则会分配3个字,多出来的1个字记录的是数组长度),其主要结构是由Mark Word 和 Class Metadata Address 组成
轻量级锁和偏向锁是Java 6 对 synchronized 锁进行优化后新增加的,重量级锁也就是通常说synchronized的对象锁,锁标识位为10,其中指针指向的是monitor对象(也称为管程或监视器锁)的起始地址。
每个对象都存在着一个 monitor 与之关联,对象与其 monitor 之间的关系有存在多种实现方式,如monitor可以与对象一起创建销毁或当线程试图获取对象锁时自动生成,但当一个 monitor 被某个线程持有后,它便处于锁定状态。
在Java虚拟机(HotSpot)中,monitor是由ObjectMonitor实现的,其主要数据结构如下(位于HotSpot虚拟机源码ObjectMonitor.hpp文件,C++实现的)
ObjectMonitor() {
_header = NULL;
_count = 0; //记录个数
_waiters = 0,
_recursions = 0;
_object = NULL;
_owner = NULL;
_WaitSet = NULL; //处于wait状态的线程,会被加入到_WaitSet
_WaitSetLock = 0 ;
_Responsible = NULL ;
_succ = NULL ;
_cxq = NULL ;
FreeNext = NULL ;
_EntryList = NULL ; //处于等待锁block状态的线程,会被加入到该列表
_SpinFreq = 0 ;
_SpinClock = 0 ;
OwnerIsThread = 0 ;
}
ObjectMonitor中有两个队列,_WaitSet 和 _EntryList,用来保存ObjectWaiter对象列表( 每个等待锁的线程都会被封装成ObjectWaiter对象),_owner指向持有ObjectMonitor对象的线程,当多个线程同时访问一段同步代码时。
首先会进入 _EntryList 集合,当线程获取到对象的monitor后进入_Owner区域并把monitor中的owner变量设置为当前线程同时monitor中的计数器count加1,若线程调用 wait() 方法,将释放当前持有的monitor,owner变量恢复为null,count自减1,同时该线程进入 WaitSet集合中等待被唤醒。
若当前线程执行完毕也将释放monitor(锁)并复位变量的值,以便其他线程进入获取monitor(锁)。
monitor对象存在于每个Java对象的对象头中(存储的指针的指向),synchronized锁便是通过这种方式获取锁的,也是为什么Java中任意对象可以作为锁的原因,同时也是notify/notifyAll/wait等方法存在于顶级对象Object中的原因
2020年3月17日JDK 14 正式发布了,其中还是有一些值得关注的新特性。如果你觉得我写的东西对于您有帮助,希望得到您的关注!
Instanceof是java中用于检查对象引用是否为给定Type类型的实例,并返回布尔值。在Java 14之前,我们在完成判断之后,经常需要做一下类型的强制转换,如下:
if (obj instanceof String) {
String str = (String) obj; // 需要强转
.. str.contains(..) ..
}else{
....
}
Java 14增强功能特性:
if (!(obj instanceof String str)) {
.. str.contains(..) .. // 不再需要转换代码,实际发生了转换
} else {
//.. str.... 这里 str 会是未定义
}
更多示例:
if (obj instanceof String str && str.length() > 5) {.. str.contains(..) ..}
if (obj instanceof String str || str.length() > 5) {.. str.contains(..) ..}
注意:仅当object不为null时,instanceOf才会匹配,然后仅将其分配给str。在instanceof中使用模式匹配可以减少Java程序中显式强制转换的数量。
在java 14之前,我们经常在调试代码的时候,发现一行代码中有多个对象,抛出异常之后你也无法确定到底是哪个对象为null。假设此代码中出现一个NullPointerException:
a.b.c.i = 0;
//下面是异常信息
Exception in thread "main" java.lang.NullPointerException
at Prog.main(Prog.java:5)
文件名和行号不能精确指出哪个变量为空。是a还是b或c?JDK14对此做了改进。
Exception in thread "main" java.lang.NullPointerException:
Cannot read field 'c' because 'a.b' is null.
at Prog.main(Prog.java:5)
但是,这也存在一些风险。空指针异常消息包含源代码中的变量名。暴露此信息可能被视为程序的安全风险。
在Java 14之前*
switch (day) {
case MONDAY:
case FRIDAY:
case SUNDAY:
System.out.println(6);
break;
case TUESDAY:
System.out.println(7);
break;
case THURSDAY:
case SATURDAY:
System.out.println(8);
break;
case WEDNESDAY:
System.out.println(9);
break;
}
Java 14增强功能
switch (day) {
case MONDAY, FRIDAY, SUNDAY -> System.out.println(6);
case TUESDAY -> System.out.println(7);
case THURSDAY, SATURDAY -> System.out.println(8);
case WEDNESDAY -> System.out.println(9);
}
Java 14使用record关键字来减少类声明语法,这有点像lombok。我们有时候需要编写许多低价值的重复代码来实现一个简单的数据载体类:构造函数,访问器,equals(),hashCode(),toString()等。为了避免这种重复代码,Java 14推出record。 java14之前的代码:
final class Point {
public final int x;
public final int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
// 很多的equals, hashCode, toString,getters、setters
}
Java 14中就用这一行代码
record Point(int x, int y) { }
在Java中,要将HTML,XML,SQL或JSON代码片段嵌入到代码中通常很难阅读,为了克服此问题,Java 14引入了Text Block。 java14之前,没有文本块的HTML示例
String html = "<html>\n" +
" <body>\n" +
" <p>Hello, world</p>\n" +
" </body>\n" +
"</html>\n";
java14,带文本块的HTML示例,下面的代码看上去是多行的,实际上字符串的拼接结果仍然是一行的。
String html = """
<html>
<body>
<p>Hello, world</p>
</body>
</html>
""";
如果你希望字符串中有换行,在每行的行尾加上“\”,这样字符串就是换行的了,可以打印出来看一下。
String html = """
<html> \
<body> \
<p>Hello, world</p> \
</body> \
</html>
""";
下面列出了当前可用的 PCRE 修饰符。括号中提到的名字是 PCRE 内部这些修饰符的名称。 模式修饰符中的空格,换行符会被忽略,其他字符会导致错误。i (PCRE_CASELESS)如果设置了这个修饰符,模式中的字母会进行大小写不敏感匹配。
m (PCRE_MULTILINE)默认情况下,PCRE 认为目标字符串是由单行字符组成的(然而实际上它可能会包含多行), “行首”元字符 (^) 仅匹配字符串的开始位置, 而”行末”元字符 ($) 仅匹配字符串末尾, 或者最后的换行符(除非设置了 D 修饰符)。这个行为和 perl 相同。 当这个修饰符设置之后,“行首”和“行末”就会匹配目标字符串中任意换行符之前或之后,另外, 还分别匹配目标字符串的最开始和最末尾位置。这等同于 perl 的 /m 修饰符。如果目标字符串 中没有 “\n” 字符,或者模式中没有出现 ^ 或 $,设置这个修饰符不产生任何影响。
s (PCRE_DOTALL)如果设置了这个修饰符,模式中的点号元字符匹配所有字符,包含换行符。如果没有这个 修饰符,点号不匹配换行符。这个修饰符等同于 perl 中的/s修饰符。 一个取反字符类比如 [^a] 总是匹配换行符,而不依赖于这个修饰符的设置。
x (PCRE_EXTENDED)如果设置了这个修饰符,模式中的没有经过转义的或不在字符类中的空白数据字符总会被忽略, 并且位于一个未转义的字符类外部的#字符和下一个换行符之间的字符也被忽略。 这个修饰符 等同于 perl 中的 /x 修饰符,使被编译模式中可以包含注释。 注意:这仅用于数据字符。 空白字符 还是不能在模式的特殊字符序列中出现,比如序列 (?( 引入了一个条件子组(译注: 这种语法定义的 特殊字符序列中如果出现空白字符会导致编译错误。 比如(?(就会导致错误)。e (PREG_REPLACE_EVAL)
Warning
This feature was DEPRECATED in PHP 5.5.0, and REMOVED as of PHP 7.0.0.如果设置了这个被弃用的修饰符, preg_replace() 在进行了对替换字符串的 后向引用替换之后, 将替换后的字符串作为php 代码评估执行(eval 函数方式),并使用执行结果 作为实际参与替换的字符串。单引号、双引号、反斜线(\)和 NULL 字符在 后向引用替换时会被用反斜线转义.
This feature was DEPRECATED in PHP 5.5.0, and REMOVED as of PHP 7.0.0.如果设置了这个被弃用的修饰符, preg_replace() 在进行了对替换字符串的 后向引用替换之后, 将替换后的字符串作为php 代码评估执行(eval 函数方式),并使用执行结果 作为实际参与替换的字符串。单引号、双引号、反斜线(\)和 NULL 字符在 后向引用替换时会被用反斜线转义.
Note:
仅 preg_replace() 使用此修饰符,其他 PCRE 函数忽略此修饰符。
A (PCRE_ANCHORED)如果设置了这个修饰符,模式被强制为”锚定”模式,也就是说约束匹配使其仅从 目标字符串的开始位置搜索。这个效果同样可以使用适当的模式构造出来,并且 这也是 perl 种实现这种模式的唯一途径。
D (PCRE_DOLLAR_ENDONLY)如果这个修饰符被设置,模式中的元字符美元符号仅仅匹配目标字符串的末尾。如果这个修饰符 没有设置,当字符串以一个换行符结尾时, 美元符号还会匹配该换行符(但不会匹配之前的任何换行符)。 如果设置了修饰符m,这个修饰符被忽略. 在 perl 中没有与此修饰符等同的修饰符。S当一个模式需要多次使用的时候,为了得到匹配速度的提升,值得花费一些时间 对其进行一些额外的分析。如果设置了这个修饰符,这个额外的分析就会执行。当前, 这种对一个模式的分析仅仅适用于非锚定模式的匹配(即没有单独的固定开始字符)。
U (PCRE_UNGREEDY)这个修饰符逆转了量词的”贪婪”模式。 使量词默认为非贪婪的,通过量词后紧跟? 的方式可以使其成为贪婪的。这和 perl 是不兼容的。 它同样可以使用 模式内修饰符设置 (?U)进行设置, 或者在量词后以问号标记其非贪婪(比如.*?)。
Note:
在非贪婪模式,通常不能匹配超过 pcre.backtrack_limit 的字符。
X (PCRE_EXTRA)这个修饰符打开了 PCRE 与 perl 不兼容的附件功能。模式中的任意反斜线后就 ingen 一个 没有特殊含义的字符都会导致一个错误,以此保留这些字符以保证向后兼容性。 默认情况下,在 perl 中,反斜线紧跟一个没有特殊含义的字符被认为是该字符的原文。 当前没有其他特性由这个修饰符控制。
J (PCRE_INFO_JCHANGED)内部选项设置(?J)修改本地的PCRE_DUPNAMES选项。允许子组重名, (译注:只能通过内部选项设置,外部的 /J 设置会产生错误。)
u (PCRE_UTF8)此修正符打开一个与 perl 不兼容的附加功能。 模式和目标字符串都被认为是 utf-8 的。 无效的目标字符串会导致 preg_* 函数什么都匹配不到; 无效的模式字符串会导致 E_WARNING 级别的错误。 PHP 5.3.4 后,5字节和6字节的 UTF-8 字符序列被考虑为无效(resp. PCRE 7.3 2007-08-28)。 以前就被认为是无效的 UTF-8。
(PCRE_INFO_JCHANGED)内部选项设置(?J)修改本地的PCRE_DUPNAMES选项。允许子组重名, (译注:只能通过内部选项设置,外部的 /J 设置会产生错误。)u (PCRE_UTF8)此修正符打开一个与 perl 不兼容的附加功能。 模式和目标字符串都被认为是 utf-8 的。 无效的目标字符串会导致 preg_* 函数什么都匹配不到; 无效的模式字符串会导致 E_WARNING 级别的错误。 PHP 5.3.4 后,5字节和6字节的 UTF-8 字符序列被考虑为无效(resp. PCRE 7.3 2007-08-28)。 以前就被认为是无效的 UTF-8。
u (PCRE_UTF8)此修正符打开一个与 perl 不兼容的附加功能。 模式和目标字符串都被认为是 utf-8 的。 无效的目标字符串会导致 preg_* 函数什么都匹配不到; 无效的模式字符串会导致 E_WARNING 级别的错误。 PHP 5.3.4 后,5字节和6字节的 UTF-8 字符序列被考虑为无效(resp. PCRE 7.3 2007-08-28)。 以前就被认为是无效的 UTF-8。
修饰符意义
/ regexp / i
不区分大小写的匹配
/ regexp / s
使句点( . )匹配任何字符,包括换行符( )
/ regexp / x
从模式中删除空白符和注释
/ regexp / m
使 ^ 匹配换行符 ( )之后的内容,美元符号($)匹配换行符 ( )之前的内容
/ regexp / e
如果替换字符串是PHP代码,使用eval()执行该代码来得到实际的替换字符串。
PHP的Perl兼容正则表达式函数也支持在Perl中不支持的其他修饰符
/ regexp / U
颠倒子模式的贪婪性; * 和 + 尽可能少地匹配而不是尽可能多。
/ regexp / u
把模式字符串当作UTF – 8编码对待
/ regexp / X
如果一个反斜杠之后跟着没有特殊意义的字符,将产生一个错误
/ regexp / A
把锚定位在字符串的开头就像模式中有 ^ 一样
/ regexp / D
使 $字符仅匹配一行的末尾
/ regexp / S
使表达式解析器更加小心地检查模式的结构,使得第二次运行时(如在一个循环中)加快速度
JAXB官网:http://jaxb.java.net/
API链接 :http://jaxb.java.net/nonav/2.2.5/docs/api/
文档 :http://jaxb.java.net/tutorial/
GC管理的主要区域是Java堆,一般情况下只针对堆进行垃圾回收。方法区、栈和本地方法区不被GC所管理,因而选择这些区域内的对象作为GC roots,被GC roots引用的对象不被GC回收。
常说的GC(Garbage Collector) roots,特指的是垃圾收集器(Garbage Collector)的对象,GC会收集那些不是GC roots且没有被GC roots引用的对象。
一个对象可以属于多个root,GC root有几下种:
看java 代码的时候,很多人都会看见用完一个树或者链表后 head = null;这样的代码。有人说为了加快gc,这里为你揭秘为什么这么写。
代码一定要跑,别的不重要。
代码一定要跑,别的不重要。
代码一定要跑,别的不重要。
怎么遍历这棵树这里就不阐述了。这里的每个节点你可以理解成每个类里的属性。
这里摘抄R大的一些回答
root大概有这些 |
---|
所有Java线程当前活跃的栈帧里指向GC堆里的对象的引用;换句话说,当前所有正在被调用的方法的引用类型的参数/局部变量/临时值。 |
VM的一些静态数据结构里指向GC堆里的对象的引用,例如说HotSpot VM里的Universe里有很多这样的引用。 |
JNI handles,包括global handles和local handles(看情况) |
所有当前被加载的Java类(看情况) |
Java类的引用类型静态变量(看情况) |
Java类的运行时常量池里的引用类型常量(String或Class类型)(看情况) |
String常量池(StringTable)里的引用 |
假设hashMap 在扩容的时候,正巧发生了gc,在标记阶段,要找出所有的root,这里的 oldTab 就是root,通过这个root,然后遍历他的所有属性,如果它里面的内容不为null 我们继续遍历内容里的对象字段是否为null,标记出所有不为null的节点。继续下图,子节点
private static Object objoct;
/**
* @author 铁拳阿牛
* @createTime 2018/7/14 下午3:33
**/
public class Node {
private Object object;
//如果这个Node被引用他的属性又被引用,属性为对象且不为null,这样一直遍历下去,
private Node node;
}
/**
* @author 铁拳阿牛
* @createTime 2018/7/14 下午3:46
*
* 请把以下参数设置到jvm里
* -Xmx4000M -Xms4000M -Xmn1300M -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=75 -XX:+PrintGCDetails
**/
public class Test {
private static final int _1MB = 1024 * 1024;
private static final int LENGTH = 40000000;
public static void main(String[] args) {
Node next = null;
for(int i = 0; i <= LENGTH; i++){
Node node = new Node(i,next);
next = node;
}
//如果不设置为null这里将会又大批量的遍历,打开这里和不打开这里,gc时间完全不一样,现在你直到为什么要设置为null了吗?
// next = null;
triggerGC();
}
/**
* 不触发gc看不见效果
* new 很多小对象。不然直接到 old区去了。
*/
private static void triggerGC(){
// byte[] all = new byte[2000 * _1MB]; //这里为什么没又直接new 一个大对象?它可能直接就到old区去了。
for(int i = 0 ; i < 500 ; i++){
byte[] bytes = new byte[2 * _1MB];
}
}
//POJO 不用看这里
static class Node {
private int valuel;
private Node node;
public Node(int valuel, Node node) {
this.valuel = valuel;
this.node = node;
}
public int getValuel() {
return valuel;
}
public void setValuel(int valuel) {
this.valuel = valuel;
}
public Node getNode() {
return node;
}
public void setNode(Node node) {
this.node = node;
}
}
}
第一次我们要把eden 的空间尽可能的占满将这两行代码注释掉,发现eden已经用了94% 很不错。
// next = null;
// triggerGC();
~~~~~~~~~~~~~~~~~
Heap
par new generation total 1198080K, used 1001063K [0x00000006c6000000, 0x0000000717400000, 0x0000000717400000)
eden space 1064960K, 94% used [0x00000006c6000000, 0x0000000703199e78, 0x0000000707000000)
from space 133120K, 0% used [0x0000000707000000, 0x0000000707000000, 0x000000070f200000)
to space 133120K, 0% used [0x000000070f200000, 0x000000070f200000, 0x0000000717400000)
concurrent mark-sweep generation total 2764800K, used 0K [0x0000000717400000, 0x00000007c0000000, 0x00000007c0000000)
Metaspace used 2674K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 288K, capacity 386K, committed 512K, reserved 1048576K
next = null;
triggerGC();
~~~~~~~~~~~~~~~~~
[GC (Allocation Failure) [ParNew: 1063732K->399K(1198080K), 0.0101658 secs] 1063732K->399K(3962880K), 0.0102629 secs] [Times: user=0.04 sys=0.00, real=0.01 secs]
Heap
par new generation total 1198080K, used 933048K [0x00000006c6000000, 0x0000000717400000, 0x0000000717400000)
eden space 1064960K, 87% used [0x00000006c6000000, 0x00000006feeca630, 0x0000000707000000)
from space 133120K, 0% used [0x000000070f200000, 0x000000070f263d38, 0x0000000717400000)
to space 133120K, 0% used [0x0000000707000000, 0x0000000707000000, 0x000000070f200000)
concurrent mark-sweep generation total 2764800K, used 0K [0x0000000717400000, 0x00000007c0000000, 0x00000007c0000000)
Metaspace used 2674K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 288K, capacity 386K, committed 512K, reserved 1048576K
// next = null;
triggerGC();
~~~~~~~~~~~~~~~~~
[GC (Allocation Failure) [ParNew: 1063732K->133119K(1198080K), 20.1710025 secs] 1063732K->938404K(3962880K), 20.1710585 secs] [Times: user=145.85 sys=1.89, real=20.17 secs]
Heap
par new generation total 1198080K, used 1065769K [0x00000006c6000000, 0x0000000717400000, 0x0000000717400000)
eden space 1064960K, 87% used [0x00000006c6000000, 0x00000006feeca630, 0x0000000707000000)
from space 133120K, 99% used [0x000000070f200000, 0x00000007173ffff8, 0x0000000717400000)
to space 133120K, 0% used [0x0000000707000000, 0x0000000707000000, 0x000000070f200000)
concurrent mark-sweep generation total 2764800K, used 805284K [0x0000000717400000, 0x00000007c0000000, 0x00000007c0000000)
Metaspace used 2676K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 288K, capacity 386K, committed 512K, reserved 1048576K
把这些对象标记后其他的都是要清除的,所以现在知道为什么要设置为null
了吗?就是为了加快标记阶段。如果你有个超大的map或者list的时候这样
做会有一点点帮助。(普通开发过程中基本没有必要考虑)
明白java的源码里为什么会有这些操作了吗?
编程语言有上千种,但是流行的不过10来种,那些我们经常使用的编程语言都是谁在什么时候创造出来的呢?Casper Beyer 为我们进行了整理。
1800年
Joseph Marie Jacquard 教会了一台织布机读穿孔卡片,制造出了第一个高度多线程的处理单元。他的发明受到了预见天网(Skynet)诞生的纺织工人的强烈反对。
1842年
Ada Lovelace(英国诗人拜伦之女)为计算程序拟定“算法”,写作的第一份“程序设计流程图”,被珍视为“第一位给计算机写程序的人”。稍微有点不便的是当时还没有计算机呢。
1936年
阿兰·图灵被称为计算机科学之父,人工智能之父。但英国法庭却并不认可,还判处对他进行化学阉割。
女皇后来宽恕了他,但不幸的是当时他已经过世很久了。
1936年
Alonzo Church(算法理论重要奠基人)发明了lambda算子,跟图灵生活在同样的时代,但是他在时代的另一边,也并没有被女王阉割。
1957年
John Backus创建了FORTRAN语言,这真正是程序员使用的第一种语言。
1959年
Grace Hopper发明了第一门针对企业面向商业的编程语言,并且把这门语言叫做“面向商业的通用语言(common business-oriented language)”,简称COBOL。
1964年
John Kemeny 和 Thomas Kurtz 认为编程太难了,需要回归本源,他们把自己的编程语言叫做BASIC。
1970年
Niklaus Wirth开发了多种语言,最后流行起来的是PASCAL。他喜欢开发语言。
他还发明了让摩尔定律变得过时的Wirth定律(软件变慢的速度比硬件变快的速度更快),因为软件开发者会编写出连大型主机也没法跟上的臃肿软件。
这在后来被证明是正确的——在Electron.js被发明出来后
1972年
Dennis Ritchie在贝尔实验室上班上到无聊了,于是他决定写出带有花括号的C语言,这门语言取得了巨大成功。随后他又增加了分段错误等对开发者友好的功能来辅助提高生产率。
折腾完这门语言之后他还有时间,于是他跟在贝尔实验室的伙计决定给C语言做个演示程序,结果他们弄出了一个操作系统,UNIX。
1980年
Alan Kay发明了一门面向对象语言,他把这门语言叫做Smalltalk,在Smalltalk中一切都是对象,甚至一个对象也是对象。没人真正搞得清楚small talk是什么意思。
1983年
Jean Ichbiah注意到Ada Lovelace的程序从来都没有实际运行过,决定以她的名字开发一门语言,但是这门语言还是没有跑起来。
1983年
Bjarne Stroustrup 注意到C在编译方面花的时间还不够多,于是他把自己能想到的每一项功能都增加了进去,然后称之为C++。
每一个地方的程序员都接受了它,因为这样他们在工作的时候找借口看阿猫阿狗视频和xkcd漫画就显得比较有诚意了。
1986年
Brac Box 和 Tol Move决定在Smalltalk的基础上制作一个C语言的不可读版本,他们把这门语言叫做Objective-C,但是没人弄得清楚它的语法。
1987年
Larry Wall有宗教经验,他成为了一名牧师,并且把Perl变成了一种教义。
1991年
Guido van Rossum不喜欢花括号,于是他发明了Python,语法选择的灵感来源自Monty Python(巨蟒剧团)和Flying Circus(飞行马戏团)。
1993年
Roberto Ierusalimschy和他的朋友认为自己需要一个巴西本地化的脚本语言,在本地化期间发生了一个错误,这个错误会把指针从1而不是0开始计算,他们把这门语言叫做Lua。
1994年
Rasmus Lerdorf给他个人主页的CGI脚本做了一个模板引擎,后来他把自己的资料都放到了网上。
世界决定将这些东西用到一切,Rasmus于是匆忙地将一些数据库绑定做了进去,并把这门语言叫做PHP。
1995年
Yukihiro Matsumoto不是很高兴,因为他注意到其他程序员不是很高兴。他创建了Ruby来让程序员高兴。在他创建了Ruby后“Matz”高兴了,Ruby社区高兴了,每个人都高兴了。
1995年
Brendan Eich利用周末时间设计了一门语言,打算用这门语言来为全世界的每一个主流浏览器乃至于最终的Skynet都提供动力。
他先是找到了Netscape然后说这门语言叫做LiveScript,但在代码评审期间Java变得流行起来,所以他们决定最好还是用花括号,然后就把它更名为JavaScript。
结果表明,Java却是一个会让他们惹上麻烦的商标,JavaScript随后更名为ECMAScript,但大家还是把它叫做JavaScript。
1996年
James Gosling发明了Java,第一们真正过于繁琐的面向对象语言,在这里设计模式完全压倒了实用主义。
于是就诞生了超级有效的管理器提供商、容器提供商、服务提供商、单一管理器提供商模式。
2001年
Anders Hejlsberg重新发明了Java然后把它叫做C#,因为用C来编程感觉要比Java酷。每个人都喜欢这个新版本的Java,因为它完全不像Java。
2005年
David Hanselmeyer Hansen创建了一个web框架叫做Ruby on Rails,从此大家不再记得Ruby和Rails是两个独立的东西了。
2006年
John Resig为JavaScript写了一个帮助库,每个人都以为那是一门语言,从此从互联网上拷贝粘贴jQuery代码就成为了一门职业。
2009年
Ken Thompson 和 Rob Pike 决定做一门类似C那样的语言,但要有更安全的装置,还要有更好的卖相,并且把Gopher(囊鼠)作为吉祥物。
他们把这门语言成为Go,并把它做成开源然后另外卖Gopher商标的护膝和头盔作为收入来源。
2010年
Graydon Hoare也想把语言做成C那样,他称之为Rust。每个人都要求马上用Rust把软件的每一块都重写一遍。Graydon希望做点更有亮点的事情,于是开始为苹果开发Swift。
2012年
Anders Hjelsberg希望在web浏览器里面写C#,于是他设计出TypeScript,这东西其实是JavaScript,但里面有了更多的Java的东西。
2013年
Jeremy Ashkenas想要像Ruby开发者一样快乐,于是他创建了CoffeeScript,这东西编译后像JavaScript但是样子又更像Ruby。Jerry从来都没有变得像Matz和Ruby开发者那样真正快乐。
2014年
Chris Lattner做Swift的时候,其主要的设计目标就是不要成为Objective-C,最后它看起来像Java。
译者:36Kr 编译组 编辑:郝鹏程。
来源:编程语言简史:有人讨厌花括号,于是他发明了Python
https://www.oschina.net/news/92787/a-brief-totally-accurate-history-of-programming-languages
Spring Framework 是一个开源的Java/Java EE全功能栈(full-stack)的应用程序框架,以Apache许可证形式发布,也有.NET平台上的移植版本。该框架基于 Expert One-on-One Java EE Design and Development(ISBN 0-7645-4385-7)一书中的代码,最初由 Rod Johnson 和 Juergen Hoeller等开发。Spring Framework 提供了一个简易的开发方式,这种开发方式,将避免那些可能致使底层代码变得繁杂混乱的大量的属性文件和帮助类。
Spring 中包含的关键特性:
在设计应用程序Model时,MVC 模式(例如Struts)通常难于给出一个简洁明了的框架结构。Spring却具有能够让这部分工作变得简单的能力。程序开发员们可以使用Spring的 JDBC 抽象层重新设计那些复杂的框架结构。
定义组件的参数-生命周期
创建期:getDefaultProps
创建期:getInitialState
创建期:componentWillMount
创建期:componentDidMount
存在期:componentWillReceiveProps
存在期:shouldComponentUpdate
存在期:componentWillUpdate
存在期:componentDidUpdate
销毁&清理期:componentWillUnmount
定义组件的参数-生命周期
生命周期相关参数,是React定义组件时提供的一系列处理函数(钓子函数),这些函数会在组件生命周期的某个阶段调用。
创建期:getDefaultProps
object getDefaultProps()
创建期:getInitialState
object getInitialState()
在组件挂载前(即:创建期)调用一次,其返回值将做为this.state的初始值。
getInitialState()方法会组件类创建的时候调用一次,其返回值会被缓存下来。该方法用于设置props属性的默认值,但仅对于非必须属性。如果父组件没有指定props中的某个值,此返回对象中的相应属性将会合并到this.props。
getInitialState()方法会在组件实例创建前调用,这时还不能使用this.props属性,且其返回对象是在所有实例间共享的。
创建期:componentWillMount
componentWillMount()
componentWillMount()服务器端和客户端都只调用一次,在初始化渲染执行之前被调用。如果在这个方法内调用setState()方法,render()方法将会收到更新后的state,也就是说这是我做在组件渲染前最后一个修改state的机会。
创建期:componentDidMount
componentDidMount()
componentDidMount()会在组件初始化(渲染完成)后立即调用一次,我们一般在这个方法中使用this.getDOMNode()方法访问原始DOM。
存在期:componentWillReceiveProps
componentWillReceiveProps(object nextProps)
componentWillReceiveProps
在将要接受新的props
时被调用
componentWillReceiveProps()方法会在组件生命周期的存在期调用,当组件感知到props属性改变,会调用此方法。render()方法将会在其后调用,这时我们可以通过this.setState()来阻止组件的再次渲染。
存在期:shouldComponentUpdate
boolean shouldComponentUpdate(object nextProps, object nextState)
shouldComponentUpdate()方法发生在组件生命周期的存在器,在组件收到新的props或state。在这个方法中,我们可以访问组件的props和state属性,通过这两个属性可以确认组件是否需要更新,如果不需要更新,则返回false,则其后的方法将不会在执行。如:
shouldComponentUpdate: function(nextProps, nextState) {
return nextProps.id !== this.props.id;
}
存在期:componentWillUpdate
componentWillUpdate(object nextProps, object nextState)
componentWillUpdate()会在收到新的props或state后调用,类似componentWillMount()。
存在期:componentDidUpdate
componentDidUpdate(object prevProps, object prevState)
componentDidUpdate()会在组件重新渲染后立即被调用,当我们需要在组件重新渲染后操作DOM则需要使用这个方法。
销毁&清理期:componentWillUnmount
componentWillUnmount()
componentWillUnmount()是组件销毁&清理期唯一调用的方法,它会在组件从DOM中移除时被调用,这时我们可以清理一些状态或清理在componentDidMount中创建的DOM元素。
官方文档:https://eggjs.org/zh-cn
登陆插件:https://www.npmjs.com/package/egg-passport
微信登陆相关:https://github.com/liyunde/egg-passport-workweixin
对于hibernate注解实体中属性对应数据库表的列名,怎么命名的问题,我们肯定不愿一个个属性去配置
Hibernaet5.1 之前 在applicationContext.xml中的sessionFactory中配置
<property name=”namingStrategy”>
<bean class=”org.hibernate.cfg.ImprovedNamingStrategy”></bean>
</property>
5.1开始
hibernate.ejb.naming_strategy将不再被支持,而是被替换成了两个属性:
hibernate.physical_naming_strategy
hibernate.implicit_naming_strategy
对于physical_naming_strategy有两个常用的配置:
org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
对于PhysicalNamingStrategyStandardImpl有DefaultNamingStrategy的效果,
对于SpringPhysicalNamingStrategy有ImprovedNamingStrategy的效果。
法1:在sessionFactory的bean里配置
<property name=”PhysicalNamingStrategy”>
<bean class=”org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl”></bean>
</property>
法2:在sessionFactory bean的hibernateProperties property中配置
<prop key=”hibernate.physical_naming_strategy”>org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl</prop>
org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy 是 spring boot 包提供地
<property name=”ImplicitNamingStrategy”>
<bean class=”org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyHbmImpl” />
</property>
或是
<prop key=”hibernate.implicit_naming_strategy”>org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyHbmImpl</prop>
Dubbo是Alibaba开源的分布式服务框架,我们可以非常容易地通过Dubbo来构建分布式服务,并根据自己实际业务应用场景来选择合适的集群容错模式,这个对于很多应用都是迫切希望的,只需要通过简单的配置就能够实现分布式服务调用,也就是说服务提供方(Provider)发布的服务可以天然就是集群服务,比如,在实时性要求很高的应用场景下,可能希望来自消费方(Consumer)的调用响应时间最短,只需要选择Dubbo的Forking Cluster模式配置,就可以对一个调用请求并行发送到多台对等的提供方(Provider)服务所在的节点上,只选择最快一个返回响应的,然后将调用结果返回给服务消费方(Consumer),显然这种方式是以冗余服务为基础的,需要消耗更多的资源,但是能够满足高实时应用的需求。
有关Dubbo服务框架的简单使用,可以参考我的其他两篇文章(《基于Dubbo的Hessian协议实现远程调用》,《Dubbo实现RPC调用使用入门》,后面参考链接中已给出链接),这里主要围绕Dubbo分布式服务相关配置的使用来说明与实践。
Dubbo服务集群容错
假设我们使用的是单机模式的Dubbo服务,如果在服务提供方(Provider)发布服务以后,服务消费方(Consumer)发出一次调用请求,恰好这次由于网络问题调用失败,那么我们可以配置服务消费方重试策略,可能消费方第二次重试调用是成功的(重试策略只需要配置即可,重试过程是透明的);但是,如果服务提供方发布服务所在的节点发生故障,那么消费方再怎么重试调用都是失败的,所以我们需要采用集群容错模式,这样如果单个服务节点因故障无法提供服务,还可以根据配置的集群容错模式,调用其他可用的服务节点,这就提高了服务的可用性。
首先,根据Dubbo文档,我们引用文档提供的一个架构图以及各组件关系说明,如下所示:
上述各个组件之间的关系(引自Dubbo文档)说明如下:
我们也简单说明目前Dubbo支持的集群容错模式,每种模式适应特定的应用场景,可以根据实际需要进行选择。Dubbo内置支持如下6种集群模式:
配置值为failover。这种模式是Dubbo集群容错默认的模式选择,调用失败时,会自动切换,重新尝试调用其他节点上可用的服务。对于一些幂等性操作可以使用该模式,如读操作,因为每次调用的副作用是相同的,所以可以选择自动切换并重试调用,对调用者完全透明。可以看到,如果重试调用必然会带来响应端的延迟,如果出现大量的重试调用,可能说明我们的服务提供方发布的服务有问题,如网络延迟严重、硬件设备需要升级、程序算法非常耗时,等等,这就需要仔细检测排查了。
例如,可以这样显式指定Failover模式,或者不配置则默认开启Failover模式,配置示例如下:
1 |
< dubbo:service interface = "org.shirdrn.dubbo.api.ChatRoomOnlineUserCounterService" version = "1.0.0" |
2 |
cluster = "failover" retries = "2" timeout = "100" ref = "chatRoomOnlineUserCounterService" protocol = "dubbo" > |
3 |
< dubbo:method name = "queryRoomUserCount" timeout = "80" retries = "2" /> |
4 |
</ dubbo:service > |
上述配置使用Failover Cluster模式,如果调用失败一次,可以再次重试2次调用,服务级别调用超时时间为100ms,调用方法queryRoomUserCount的超时时间为80ms,允许重试2次,最坏情况调用花费时间160ms。如果该服务接口org.shirdrn.dubbo.api.ChatRoomOnlineUserCounterService还有其他的方法可供调用,则其他方法没有显式配置则会继承使用dubbo:service配置的属性值。
配置值为failfast。这种模式称为快速失败模式,调用只执行一次,失败则立即报错。这种模式适用于非幂等性操作,每次调用的副作用是不同的,如写操作,比如交易系统我们要下订单,如果一次失败就应该让它失败,通常由服务消费方控制是否重新发起下订单操作请求(另一个新的订单)。
配置值为failsafe。失败安全模式,如果调用失败, 则直接忽略失败的调用,而是要记录下失败的调用到日志文件,以便后续审计。
配置值为failback。失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。
配置值为forking。并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。
配置值为broadcast。广播调用所有提供者,逐个调用,任意一台报错则报错(2.1.0开始支持)。通常用于通知所有提供者更新缓存或日志等本地资源信息。
上面的6种模式都可以应用于生产环境,我们可以根据实际应用场景选择合适的集群容错模式。如果我们觉得Dubbo内置提供的几种集群容错模式都不能满足应用需要,也可以定制实现自己的集群容错模式,因为Dubbo框架给我提供的扩展的接口,只需要实现接口com.alibaba.dubbo.rpc.cluster.Cluster即可,接口定义如下所示:
01 |
@SPI (FailoverCluster.NAME) |
02 |
public interface Cluster { |
03 |
04 |
/** |
05 |
* Merge the directory invokers to a virtual invoker. |
06 |
* @param <T> |
07 |
* @param directory |
08 |
* @return cluster invoker |
09 |
* @throws RpcException |
10 |
*/ |
11 |
@Adaptive |
12 |
<T> Invoker<T> join(Directory<T> directory) throws RpcException; |
13 |
14 |
} |
关于如何实现一个自定义的集群容错模式,可以参考Dubbo源码中内置支持的汲取你容错模式的实现,6种模式对应的实现类如下所示:
1 |
com.alibaba.dubbo.rpc.cluster.support.FailoverCluster |
2 |
com.alibaba.dubbo.rpc.cluster.support.FailfastCluster |
3 |
com.alibaba.dubbo.rpc.cluster.support.FailsafeCluster |
4 |
com.alibaba.dubbo.rpc.cluster.support.FailbackCluster |
5 |
com.alibaba.dubbo.rpc.cluster.support.ForkingCluster |
6 |
com.alibaba.dubbo.rpc.cluster.support.AvailableCluster |
可能我们初次接触Dubbo时,不知道如何在实际开发过程中使用Dubbo的集群模式,后面我们会以Failover Cluster模式为例开发我们的分布式应用,再进行详细的介绍。
Dubbo服务负载均衡
Dubbo框架内置提供负载均衡的功能以及扩展接口,我们可以透明地扩展一个服务或服务集群,根据需要非常容易地增加/移除节点,提高服务的可伸缩性。Dubbo框架内置提供了4种负载均衡策略,如下所示:
在实际使用中,只需要选择合适的负载均衡策略值,配置即可,下面是上述四种负载均衡策略配置的示例:
1 |
< dubbo:service interface = "org.shirdrn.dubbo.api.ChatRoomOnlineUserCounterService" version = "1.0.0" |
2 |
cluster = "failover" retries = "2" timeout = "100" loadbalance = "random" |
3 |
ref = "chatRoomOnlineUserCounterService" protocol = "dubbo" > |
4 |
< dubbo:method name = "queryRoomUserCount" timeout = "80" retries = "2" loadbalance = "leastactive" /> |
5 |
</ dubbo:service > |
上述配置,也体现了Dubbo配置的继承性特点,也就是dubbo:service元素配置了loadbalance=”random”,则该元素的子元素dubbo:method如果没有指定负载均衡策略,则默认为loadbalance=”random”,否则如果dubbo:method指定了loadbalance=”leastactive”,则使用子元素配置的负载均衡策略覆盖了父元素指定的策略(这里调用queryRoomUserCount方法使用leastactive负载均衡策略)。
当然,Dubbo框架也提供了实现自定义负载均衡策略的接口,可以实现com.alibaba.dubbo.rpc.cluster.LoadBalance接口,接口定义如下所示:
01 |
/** |
02 |
* LoadBalance. (SPI, Singleton, ThreadSafe) |
03 |
* |
04 |
* <a href="http://en.wikipedia.org/wiki/Load_balancing_(computing)">Load-Balancing</a> |
05 |
* |
06 |
* @see com.alibaba.dubbo.rpc.cluster.Cluster#join(Directory) |
07 |
* @author qian.lei |
08 |
* @author william.liangf |
09 |
*/ |
10 |
@SPI (RandomLoadBalance.NAME) |
11 |
public interface LoadBalance { |
12 |
13 |
/** |
14 |
* select one invoker in list. |
15 |
* @param invokers invokers. |
16 |
* @param url refer url |
17 |
* @param invocation invocation. |
18 |
* @return selected invoker. |
19 |
*/ |
20 |
@Adaptive ( "loadbalance" ) |
21 |
<T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException; |
22 |
23 |
} |
如何实现一个自定义负载均衡策略,可以参考Dubbo框架内置的实现,如下所示的3个实现类:
1 |
com.alibaba.dubbo.rpc.cluster.loadbalance.RandomLoadBalance |
2 |
com.alibaba.dubbo.rpc.cluster.loadbalance.RoundRobinLoadBalance |
3 |
com.alibaba.dubbo.rpc.cluster.loadbalance.LeastActiveLoadBalance |
Dubbo服务集群容错实践
手机应用是以聊天室为基础的,我们需要收集用户的操作行为,然后计算聊天室中在线人数,并实时在手机应用端显示人数,整个系统的架构如图所示:
上图中,主要包括了两大主要流程:日志收集并实时处理流程、调用读取实时计算结果流程,我们使用基于Dubbo框架开发的服务来提供实时计算结果读取聊天人数的功能。上图中,实际上业务接口服务器集群也可以基于Dubbo框架构建服务,就看我们想要构建什么样的系统来满足我们的需要。
如果不使用注册中心,服务消费方也能够直接调用服务提供方发布的服务,这样需要服务提供方将服务地址暴露给服务消费方,而且也无法使用监控中心的功能,这种方式成为直连。
如果我们使用注册中心,服务提供方将服务发布到注册中心,而服务消费方可以通过注册中心订阅服务,接收服务提供方服务变更通知,这种方式可以隐藏服务提供方的细节,包括服务器地址等敏感信息,而服务消费方只能通过注册中心来获取到已注册的提供方服务,而不能直接跨过注册中心与服务提供方直接连接。这种方式的好处是还可以使用监控中心服务,能够对服务的调用情况进行监控分析,还能使用Dubbo服务管理中心,方便管理服务,我们在这里使用的是这种方式,也推荐使用这种方式。使用注册中心的Dubbo分布式服务相关组件结构,如下图所示:
下面,开发部署我们的应用,通过如下4个步骤来完成:
服务接口将服务提供方(Provider)和服务消费方(Consumer)连接起来,服务提供方实现接口中定义的服务,即给出服务的实现,而服务消费方负责调用服务。我们接口中给出了2个方法,一个是实时查询获取当前聊天室内人数,另一个是查询一天中某个/某些聊天室中在线人数峰值,接口定义如下所示:
01 |
package org.shirdrn.dubbo.api; |
02 |
03 |
import java.util.List; |
04 |
05 |
public interface ChatRoomOnlineUserCounterService { |
06 |
07 |
String queryRoomUserCount(String rooms); |
08 |
|
09 |
List<String> getMaxOnlineUserCount(List<String> rooms, String date, String dateFormat); |
10 |
} |
接口是服务提供方和服务消费方公共遵守的协议,一般情况下是服务提供方将接口定义好后提供给服务消费方。
服务提供方实现接口中定义的服务,其实现和普通的服务没什么区别,我们的实现类为ChatRoomOnlineUserCounterServiceImpl,代码如下所示:
01 |
package org.shirdrn.dubbo.provider.service; |
02 |
03 |
import java.util.List; |
04 |
05 |
import org.apache.commons.logging.Log; |
06 |
import org.apache.commons.logging.LogFactory; |
07 |
import org.shirdrn.dubbo.api.ChatRoomOnlineUserCounterService; |
08 |
import org.shirdrn.dubbo.common.utils.DateTimeUtils; |
09 |
10 |
import redis.clients.jedis.Jedis; |
11 |
import redis.clients.jedis.JedisPool; |
12 |
13 |
import com.alibaba.dubbo.common.utils.StringUtils; |
14 |
import com.google.common.base.Strings; |
15 |
import com.google.common.collect.Lists; |
16 |
17 |
public class ChatRoomOnlineUserCounterServiceImpl implements ChatRoomOnlineUserCounterService { |
18 |
19 |
private static final Log LOG = LogFactory.getLog(ChatRoomOnlineUserCounterServiceImpl. class ); |
20 |
private JedisPool jedisPool; |
21 |
private static final String KEY_USER_COUNT = "chat::room::play::user::cnt" ; |
22 |
private static final String KEY_MAX_USER_COUNT_PREFIX = "chat::room::max::user::cnt::" ; |
23 |
private static final String DF_YYYYMMDD = "yyyyMMdd" ; |
24 |
25 |
public String queryRoomUserCount(String rooms) { |
26 |
LOG.info( "Params[Server|Recv|REQ] rooms=" + rooms); |
27 |
StringBuffer builder = new StringBuffer(); |
28 |
if (!Strings.isNullOrEmpty(rooms)) { |
29 |
Jedis jedis = null ; |
30 |
try { |
31 |
jedis = jedisPool.getResource(); |
32 |
String[] fields = rooms.split( "," ); |
33 |
List<String> results = jedis.hmget(KEY_USER_COUNT, fields); |
34 |
builder.append(StringUtils.join(results, "," )); |
35 |
} catch (Exception e) { |
36 |
LOG.error( "" , e); |
37 |
} finally { |
38 |
if (jedis != null ) { |
39 |
jedis.close(); |
40 |
} |
41 |
} |
42 |
} |
43 |
LOG.info( "Result[Server|Recv|RES] " + builder.toString()); |
44 |
return builder.toString(); |
45 |
} |
46 |
|
47 |
@Override |
48 |
public List<String> getMaxOnlineUserCount(List<String> rooms, String date, String dateFormat) { |
49 |
// HGETALL chat::room::max::user::cnt::20150326 |
50 |
LOG.info( "Params[Server|Recv|REQ] rooms=" + rooms + ",date=" + date + ",dateFormat=" + dateFormat); |
51 |
String whichDate = DateTimeUtils.format(date, dateFormat, DF_YYYYMMDD); |
52 |
String key = KEY_MAX_USER_COUNT_PREFIX + whichDate; |
53 |
StringBuffer builder = new StringBuffer(); |
54 |
if (rooms != null && !rooms.isEmpty()) { |
55 |
Jedis jedis = null ; |
56 |
try { |
57 |
jedis = jedisPool.getResource(); |
58 |
return jedis.hmget(key, rooms.toArray( new String[rooms.size()])); |
59 |
} catch (Exception e) { |
60 |
LOG.error( "" , e); |
61 |
} finally { |
62 |
if (jedis != null ) { |
63 |
jedis.close(); |
64 |
} |
65 |
} |
66 |
} |
67 |
LOG.info( "Result[Server|Recv|RES] " + builder.toString()); |
68 |
return Lists.newArrayList(); |
69 |
} |
70 |
|
71 |
public void setJedisPool(JedisPool jedisPool) { |
72 |
this .jedisPool = jedisPool; |
73 |
} |
74 |
75 |
} |
代码中通过读取Redis中数据来完成调用,逻辑比较简单。对应的Maven POM依赖配置,如下所示:
01 |
< dependencies > |
02 |
< dependency > |
03 |
< groupId >org.shirdrn.dubbo</ groupId > |
04 |
< artifactId >dubbo-api</ artifactId > |
05 |
< version >0.0.1-SNAPSHOT</ version > |
06 |
</ dependency > |
07 |
< dependency > |
08 |
< groupId >org.shirdrn.dubbo</ groupId > |
09 |
< artifactId >dubbo-commons</ artifactId > |
10 |
< version >0.0.1-SNAPSHOT</ version > |
11 |
</ dependency > |
12 |
< dependency > |
13 |
< groupId >redis.clients</ groupId > |
14 |
< artifactId >jedis</ artifactId > |
15 |
< version >2.5.2</ version > |
16 |
</ dependency > |
17 |
< dependency > |
18 |
< groupId >org.apache.commons</ groupId > |
19 |
< artifactId >commons-pool2</ artifactId > |
20 |
< version >2.2</ version > |
21 |
</ dependency > |
22 |
< dependency > |
23 |
< groupId >org.jboss.netty</ groupId > |
24 |
< artifactId >netty</ artifactId > |
25 |
< version >3.2.7.Final</ version > |
26 |
</ dependency > |
27 |
</ dependencies > |
有关对Dubbo框架的一些依赖,我们单独放到一个通用的Maven Module中(详见后面“附录:Dubbo使用Maven构建依赖配置”),这里不再多说。服务提供方实现,最关键的就是服务的配置,因为Dubbo基于Spring来管理配置和实例,所以通过配置可以指定服务是否是分布式服务,以及通过配置增加很多其它特性。我们的配置文件为provider-cluster.xml,内容如下所示:
01 |
<? xml version = "1.0" encoding = "UTF-8" ?> |
02 |
03 |
< beans xmlns = "http://www.springframework.org/schema/beans" |
04 |
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo = "http://code.alibabatech.com/schema/dubbo" |
05 |
xmlns:p = "http://www.springframework.org/schema/p" |
06 |
xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.0.xsd |
07 |
http://code.alibabatech.com/schema/dubbohttp://code.alibabatech.com/schema/dubbo/dubbo.xsd"> |
08 |
09 |
< bean class = "org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" > |
10 |
< property name = "systemPropertiesModeName" value = "SYSTEM_PROPERTIES_MODE_OVERRIDE" /> |
11 |
< property name = "ignoreResourceNotFound" value = "true" /> |
12 |
< property name = "locations" > |
13 |
< list > |
14 |
< value >classpath*:jedis.properties</ value > |
15 |
</ list > |
16 |
</ property > |
17 |
</ bean > |
18 |
|
19 |
< dubbo:application name = "chatroom-cluster-provider" /> |
20 |
< dubbo:registry address = "zookeeper://zk1:2181?backup=zk2:2181,zk3:2181" /> |
21 |
|
22 |
< dubbo:protocol name = "dubbo" port = "20880" /> |
23 |
|
24 |
< dubbo:service interface = "org.shirdrn.dubbo.api.ChatRoomOnlineUserCounterService" version = "1.0.0" |
25 |
cluster = "failover" retries = "2" timeout = "1000" loadbalance = "random" actives = "100" executes = "200" |
26 |
ref = "chatRoomOnlineUserCounterService" protocol = "dubbo" > |
27 |
< dubbo:method name = "queryRoomUserCount" timeout = "500" retries = "2" loadbalance = "roundrobin" actives = "50" /> |
28 |
</ dubbo:service > |
29 |
|
30 |
< bean id = "chatRoomOnlineUserCounterService" class = "org.shirdrn.dubbo.provider.service.ChatRoomOnlineUserCounterServiceImpl" > |
31 |
< property name = "jedisPool" ref = "jedisPool" /> |
32 |
</ bean > |
33 |
|
34 |
< bean id = "jedisPool" class = "redis.clients.jedis.JedisPool" destroy-method = "destroy" > |
35 |
< constructor-arg index = "0" > |
36 |
< bean class = "org.apache.commons.pool2.impl.GenericObjectPoolConfig" > |
37 |
< property name = "maxTotal" value = "${redis.pool.maxTotal}" /> |
38 |
< property name = "maxIdle" value = "${redis.pool.maxIdle}" /> |
39 |
< property name = "minIdle" value = "${redis.pool.minIdle}" /> |
40 |
< property name = "maxWaitMillis" value = "${redis.pool.maxWaitMillis}" /> |
41 |
< property name = "testOnBorrow" value = "${redis.pool.testOnBorrow}" /> |
42 |
< property name = "testOnReturn" value = "${redis.pool.testOnReturn}" /> |
43 |
< property name = "testWhileIdle" value = "true" /> |
44 |
</ bean > |
45 |
</ constructor-arg > |
46 |
< constructor-arg index = "1" value = "${redis.host}" /> |
47 |
< constructor-arg index = "2" value = "${redis.port}" /> |
48 |
< constructor-arg index = "3" value = "${redis.timeout}" /> |
49 |
</ bean > |
50 |
|
51 |
</ beans > |
上面配置中,使用dubbo协议,集群容错模式为failover,服务级别负载均衡策略为random,方法级别负载均衡策略为roundrobin(它覆盖了服务级别的配置内容),其他一些配置内容可以参考Dubbo文档。我们这里是从Redis读取数据,所以使用了Redis连接池。
启动服务示例代码如下所示:
01 |
package org.shirdrn.dubbo.provider; |
02 |
03 |
import org.shirdrn.dubbo.provider.common.DubboServer; |
04 |
05 |
public class ChatRoomClusterServer { |
06 |
07 |
public static void main(String[] args) throws Exception { |
08 |
DubboServer.startServer( "classpath:provider-cluster.xml" ); |
09 |
} |
10 |
11 |
} |
上面调用了DubboServer类的静态方法startServer,如下所示:
01 |
public static void startServer(String config) { |
02 |
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config); |
03 |
try { |
04 |
context.start(); |
05 |
System.in.read(); |
06 |
} catch (IOException e) { |
07 |
e.printStackTrace(); |
08 |
} finally { |
09 |
context.close(); |
10 |
} |
11 |
} |
方法中主要是初始化Spring IoC容器,全部对象都交由容器来管理。
服务消费方就容易了,只需要知道注册中心地址,并引用服务提供方提供的接口,消费方调用服务实现如下所示:
01 |
package org.shirdrn.dubbo.consumer; |
02 |
03 |
import java.util.Arrays; |
04 |
import java.util.List; |
05 |
06 |
import org.apache.commons.logging.Log; |
07 |
import org.apache.commons.logging.LogFactory; |
08 |
import org.shirdrn.dubbo.api.ChatRoomOnlineUserCounterService; |
09 |
import org.springframework.context.support.AbstractXmlApplicationContext; |
10 |
import org.springframework.context.support.ClassPathXmlApplicationContext; |
11 |
12 |
public class ChatRoomDubboConsumer { |
13 |
14 |
private static final Log LOG = LogFactory.getLog(ChatRoomDubboConsumer. class ); |
15 |
|
16 |
public static void main(String[] args) throws Exception { |
17 |
AbstractXmlApplicationContext context = new ClassPathXmlApplicationContext( "classpath:consumer.xml" ); |
18 |
try { |
19 |
context.start(); |
20 |
ChatRoomOnlineUserCounterService chatRoomOnlineUserCounterService = (ChatRoomOnlineUserCounterService) context.getBean( "chatRoomOnlineUserCounterService" ); |
21 |
getMaxOnlineUserCount(chatRoomOnlineUserCounterService); |
22 |
getRealtimeOnlineUserCount(chatRoomOnlineUserCounterService); |
23 |
System.in.read(); |
24 |
} finally { |
25 |
context.close(); |
26 |
} |
27 |
|
28 |
} |
29 |
30 |
private static void getMaxOnlineUserCount(ChatRoomOnlineUserCounterService liveRoomOnlineUserCountService) { |
31 |
List<String> maxUserCounts = liveRoomOnlineUserCountService.getMaxOnlineUserCount( |
32 |
Arrays.asList( new String[] { "1482178010" , "1408492761" , "1430546839" , "1412517075" , "1435861734" }), "20150327" , "yyyyMMdd" ); |
33 |
LOG.info( "After getMaxOnlineUserCount invoked: maxUserCounts= " + maxUserCounts); |
34 |
} |
35 |
36 |
private static void getRealtimeOnlineUserCount(ChatRoomOnlineUserCounterService liveRoomOnlineUserCountService) |
37 |
throws InterruptedException { |
38 |
String rooms = "1482178010,1408492761,1430546839,1412517075,1435861734" ; |
39 |
String onlineUserCounts = liveRoomOnlineUserCountService.queryRoomUserCount(rooms); |
40 |
LOG.info( "After queryRoomUserCount invoked: onlineUserCounts= " + onlineUserCounts); |
41 |
} |
42 |
} |
对应的配置文件为consumer.xml,内容如下所示:
01 |
<? xml version = "1.0" encoding = "UTF-8" ?> |
02 |
03 |
< beans xmlns = "http://www.springframework.org/schema/beans" |
04 |
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo = "http://code.alibabatech.com/schema/dubbo" |
05 |
xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.0.xsd |
06 |
http://code.alibabatech.com/schema/dubbohttp://code.alibabatech.com/schema/dubbo/dubbo.xsd"> |
07 |
08 |
< dubbo:application name = "chatroom-consumer" /> |
09 |
< dubbo:registry address = "zookeeper://zk1:2181?backup=zk2:2181,zk3:2181" /> |
10 |
|
11 |
< dubbo:reference id = "chatRoomOnlineUserCounterService" interface = "org.shirdrn.dubbo.api.ChatRoomOnlineUserCounterService" version = "1.0.0" > |
12 |
< dubbo:method name = "queryRoomUserCount" retries = "2" /> |
13 |
</ dubbo:reference > |
14 |
15 |
</ beans > |
也可以根据需要配置dubbo:reference相关的属性值,也可以配置dubbo:method指定调用的方法的配置信息,详细配置属性可以参考Dubbo官方文档。
开发完成提供方服务后,在本地开发调试的时候可以怎么简单怎么做,如果是要部署到生产环境,则需要打包后进行部署,可以参考下面的Maven POM配置:
01 |
< build > |
02 |
< plugins > |
03 |
< plugin > |
04 |
< groupId >org.apache.maven.plugins</ groupId > |
05 |
< artifactId >maven-shade-plugin</ artifactId > |
06 |
< version >1.4</ version > |
07 |
< configuration > |
08 |
< createDependencyReducedPom >true</ createDependencyReducedPom > |
09 |
</ configuration > |
10 |
< executions > |
11 |
< execution > |
12 |
< phase >package</ phase > |
13 |
< goals > |
14 |
< goal >shade</ goal > |
15 |
</ goals > |
16 |
< configuration > |
17 |
< transformers > |
18 |
< transformer implementation = "org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" /> |
19 |
< transformer implementation = "org.apache.maven.plugins.shade.resource.ManifestResourceTransformer" > |
20 |
< mainClass >org.shirdrn.dubbo.provider.ChatRoomClusterServer</ mainClass > |
21 |
</ transformer > |
22 |
</ transformers > |
23 |
</ configuration > |
24 |
</ execution > |
25 |
</ executions > |
26 |
</ plugin > |
27 |
</ plugins > |
28 |
</ build > |
这里也给出Maven POM依赖的简单配置:
1 |
< dependencies > |
2 |
< dependency > |
3 |
< groupId >org.shirdrn.dubbo</ groupId > |
4 |
< artifactId >dubbo-api</ artifactId > |
5 |
< version >0.0.1-SNAPSHOT</ version > |
6 |
</ dependency > |
7 |
</ dependencies > |
我们开发的服务应该是分布式的,首先是通过配置内容来决定,例如设置集群模式、设置负载均衡模式等,然后在部署的时候,可以在多个节点上同一个服务,这样多个服务都会注册到Dubbo注册中心,如果某个节点上的服务不可用了,可以根据我们配置的策略来选择其他节点上的可用服务,后面通过Dubbo服务管理中心和监控中心就能更加清楚明了。
来源:http://shiyanjun.cn/archives/1075.html
Update Struts dependencies to 2.5.
Remove the following plugin dependencies because they were dropped and aren’t supported anymore.
The org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
was moved to org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter
.
In web.xml replace this:
< filter > < filter-name >struts2</ filter-name > < filter-class >org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</ filter-class > </ filter > |
with that:
< filter > < filter-name >struts2</ filter-name > < filter-class >org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</ filter-class > </ filter > |
There were other package changes, please read Version Notes 2.5 for more details.
Struts DTD was updated to 2.5 version.
In struts.xml replace 2.3 DTD version:
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" |
with 2.5:
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.5//EN" |
The id
attribute was replaced with var
attribute in the following tags.
<s:action>
<s:append>
<s:bean>
<s:date>
<s:generator>
<s:iterator>
<s:merge>
<s:number>
<s:set>
<s:sort>
<s:subset>
<s:text>
<s:url>
If you have something like that in your code:
< s:url id = "url" action = "login" > |
change it to:
< s:url var = "url" action = "login" > |
The <s:set>
tag name
attribute is replaced with var
attribute.
From:
< s:set id = "str1" value = "'string1 value'" /> < s:set name = "str2" value = "'string2 value'" /> |
to:
< s:set var = "str1" value = "'string1 value'" /> < s:set var = "str2" value = "'string2 value'" /> |
Also escape
attribute was renamed to escapeHtml
attribute.
From:
< s:property escape = "true" var = "someProperty" /> |
to:
< s:property escapeHtml = "true" var = "someProperty" /> |
The <s:div>
tag was dropped.
Replace <s:div>
with plain HTML <div>
tag.
If you have field names which starts with single lower case letter, for example:
private String sTrng; public String getSTrng() {...} public void setSTrng(String str) {...} |
change accessors to getsTrng
and setsTrng
.
Or better yet, change field names to not contain single lower case letter:
private String strng; public String getStrng() {...} public void setStrng(String str) {...} |
For additional info see WW-3909.
Depending on from which version of struts you upgrade and whether you used tiles-plugin
or tiles3-plugin
you may need to do different steps.
Struts 2.5 just provides a tiles-plugin
which uses Tiles3. So support for Tiles2 has been dropped as well as the name tiles3-plugin
.
Now the only maven dependency looks like this:
< dependency > < groupId >org.apache.struts</ groupId > < artifactId >struts2-tiles-plugin</ artifactId > < version >${struts2.version}</ version > </ dependency > |
You may need to update DTD in your tiles.xml
files to Tiles3:
<!DOCTYPE tiles-definitions PUBLIC "-//Apache Software Foundation//DTD Tiles Configuration 3.0//EN" |
A Listener in web.xml
is required. It is not necessary to configure paths to tiles.xml
files here as they are picked up automatically.
< listener > < listener-class >org.apache.struts2.tiles.StrutsTilesListener</ listener-class > </ listener > |
Optionally you may remove TilesDefinitions from XML and annotate actions instead. See Tiles Plugin for more details.
Users reported it was necessary for them to remove temp/work directory of their ApplicationServer/ServletContainer. Likely to force server to recompile JSPs.
来源:https://cwiki.apache.org/confluence/display/WW/Struts%202.3%20to%202.5%20migration
变量就是申请内存来存储值。也就是说,当创建变量的时候,需要在内存中申请空间。
内存管理系统根据变量的类型为变量分配存储空间,分配的空间只能用来储存该类型数据。
因此,通过定义不同类型的变量,可以在内存中储存整数、小数或者字符。
Java的两大数据类型:
Java语言提供了八种基本类型。六种数字类型(四个整数型(默认是int 型),两个浮点型(默认是double 型)),一种字符类型,还有一种布尔型。
byte:
short:
int:
long:
char 类型可以参与整型计算,然后转换成字符型
对于数值类型的基本类型的取值范围,我们无需强制去记忆,因为它们的值都已经以常量的形式定义在对应的包装类中了。请看下面的例子:
public class PrimitiveTypeTest {
public static void main(String[] args) {
// byte
System.out.println("基本类型:byte 二进制位数:" + Byte.SIZE);
System.out.println("包装类:java.lang.Byte");
System.out.println("最小值:Byte.MIN_VALUE=" + Byte.MIN_VALUE);
System.out.println("最大值:Byte.MAX_VALUE=" + Byte.MAX_VALUE);
System.out.println();
// short
System.out.println("基本类型:short 二进制位数:" + Short.SIZE);
System.out.println("包装类:java.lang.Short");
System.out.println("最小值:Short.MIN_VALUE=" + Short.MIN_VALUE);
System.out.println("最大值:Short.MAX_VALUE=" + Short.MAX_VALUE);
System.out.println();
// int
System.out.println("基本类型:int 二进制位数:" + Integer.SIZE);
System.out.println("包装类:java.lang.Integer");
System.out.println("最小值:Integer.MIN_VALUE=" + Integer.MIN_VALUE);
System.out.println("最大值:Integer.MAX_VALUE=" + Integer.MAX_VALUE);
System.out.println();
// long
System.out.println("基本类型:long 二进制位数:" + Long.SIZE);
System.out.println("包装类:java.lang.Long");
System.out.println("最小值:Long.MIN_VALUE=" + Long.MIN_VALUE);
System.out.println("最大值:Long.MAX_VALUE=" + Long.MAX_VALUE);
System.out.println();
// float
System.out.println("基本类型:float 二进制位数:" + Float.SIZE);
System.out.println("包装类:java.lang.Float");
System.out.println("最小值:Float.MIN_VALUE=" + Float.MIN_VALUE);
System.out.println("最大值:Float.MAX_VALUE=" + Float.MAX_VALUE);
System.out.println();
// double
System.out.println("基本类型:double 二进制位数:" + Double.SIZE);
System.out.println("包装类:java.lang.Double");
System.out.println("最小值:Double.MIN_VALUE=" + Double.MIN_VALUE);
System.out.println("最大值:Double.MAX_VALUE=" + Double.MAX_VALUE);
System.out.println();
// char
System.out.println("基本类型:char 二进制位数:" + Character.SIZE);
System.out.println("包装类:java.lang.Character");
// 以数值形式而不是字符形式将Character.MIN_VALUE输出到控制台
System.out.println("最小值:Character.MIN_VALUE="
+ (int) Character.MIN_VALUE);
// 以数值形式而不是字符形式将Character.MAX_VALUE输出到控制台
System.out.println("最大值:Character.MAX_VALUE="
+ (int) Character.MAX_VALUE);
}
}
编译以上代码输出结果如下所示:
基本类型:byte 二进制位数:8
包装类:java.lang.Byte
最小值:Byte.MIN_VALUE=-128
最大值:Byte.MAX_VALUE=127
基本类型:short 二进制位数:16
包装类:java.lang.Short
最小值:Short.MIN_VALUE=-32768
最大值:Short.MAX_VALUE=32767
基本类型:int 二进制位数:32
包装类:java.lang.Integer
最小值:Integer.MIN_VALUE=-2147483648
最大值:Integer.MAX_VALUE=2147483647
基本类型:long 二进制位数:64
包装类:java.lang.Long
最小值:Long.MIN_VALUE=-9223372036854775808
最大值:Long.MAX_VALUE=9223372036854775807
基本类型:float 二进制位数:32
包装类:java.lang.Float
最小值:Float.MIN_VALUE=1.4E-45
最大值:Float.MAX_VALUE=3.4028235E38
基本类型:double 二进制位数:64
包装类:java.lang.Double
最小值:Double.MIN_VALUE=4.9E-324
最大值:Double.MAX_VALUE=1.7976931348623157E308
基本类型:char 二进制位数:16
包装类:java.lang.Character
最小值:Character.MIN_VALUE=0
最大值:Character.MAX_VALUE=65535
Float和Double的最小值和最大值都是以科学记数法的形式输出的,结尾的”E+数字”表示E之前的数字要乘以10的多少倍。比如3.14E3就是3.14×1000=3140,3.14E-3就是3.14/1000=0.00314。
实际上,JAVA中还存在另外一种基本类型void,它也有对应的包装类 java.lang.Void,不过我们无法直接对它们进行操作。
常量就是一个固定值。它们不需要计算,直接代表相应的值。
常量指不能改变的量。 在Java中用final标志,声明方式和变量类似:
final double PI = 3.1415927;
虽然常量名也可以用小写,但为了便于识别,通常使用大写字母表示常量。
字面量可以赋给任何内置类型的变量。例如:
byte a = 68;
char a = 'A'
byte、int、long、和short都可以用十进制、16进制以及8进制的方式来表示。
当使用常量的时候,前缀0表示8进制,而前缀0x代表16进制。例如:
int decimal = 100;
int octal = 0144;
int hexa = 0x64;
和其他语言一样,Java的字符串常量也是包含在两个引号之间的字符序列。下面是字符串型字面量的例子:
"Hello World"
"two\nlines"
"\"This is in quotes\""
字符串常量和字符常量都可以包含任何Unicode字符。例如:
char a = '\u0001';
String a = "\u0001";
Java语言支持一些特殊的转义字符序列。
符号 | 字符含义 |
---|---|
\n | 换行 (0x0a) |
\r | 回车 (0x0d) |
\f | 换页符(0x0c) |
\b | 退格 (0x08) |
\s | 空格 (0x20) |
\t | 制表符 |
\” | 双引号 |
\’ | 单引号 |
\\ | 反斜杠 |
\ddd | 八进制字符 (ddd) |
\uxxxx | 16进制Unicode字符 (xxxx) |
参考:https://issues.apache.org/jira/browse/LOG4J2-411
It would be very nice, if the the XML configuration uses an dedicated namespace, e.g. http://logging.apache.org/log4j/2.0/config.
This feature allows using XML catalogs to locate the schema locally, e.g. with Eclipse IDE.
The Log4j-events.xsd contains already such a declaration:
targetNamespace="http://logging.apache.org/log4j/2.0/events"
Then the configuration XML file needs only a small extension:
<?xml version="1.0" encoding="utf-8"?> <Configuration status="WARN" xmlns="http://logging.apache.org/log4j/2.0/config"> <Appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" /> </Console> </Appenders> <Loggers> <Root level="error"> <AppenderRef ref="Console" /> </Root> </Loggers> </Configuration>
IO的方式通常分为几种,同步阻塞的BIO、同步非阻塞的NIO、异步非阻塞的AIO。
一、BIO
在JDK1.4出来之前,我们建立网络连接的时候采用BIO模式,需要先在服务端启动一个ServerSocket,然后在客户端启动Socket来对服务端进行通信,默认情况下服务端需要对每个请求建立一堆线程等待请求,而客户端发送请求后,先咨询服务端是否有线程相应,如果没有则会一直等待或者遭到拒绝请求,如果有的话,客户端会线程会等待请求结束后才继续执行。
二、NIO (New IO)
NIO本身是基于事件驱动思想来完成的,其主要想解决的是BIO的大并发问题: 在使用同步I/O的网络应用中,如果要同时处理多个客户端请求,或是在客户端要同时和多个服务器进行通讯,就必须使用多线程来处理。也就是说,将每一个客户端请求分配给一个线程来单独处理。这样做虽然可以达到我们的要求,但同时又会带来另外一个问题。由于每创建一个线程,就要为这个线程分配一定的内存空间(也叫工作存储器),而且操作系统本身也对线程的总数有一定的限制。如果客户端的请求过多,服务端程序可能会因为不堪重负而拒绝客户端的请求,甚至服务器可能会因此而瘫痪。
NIO基于Reactor,当socket有流可读或可写入socket时,操作系统会相应的通知引用程序进行处理,应用再将流读取到缓冲区或写入操作系统。 也就是说,这个时候,已经不是一个连接就要对应一个处理线程了,而是有效的请求,对应一个线程,当连接没有数据时,是没有工作线程来处理的。
BIO与NIO一个比较重要的不同,是我们使用BIO的时候往往会引入多线程,每个连接一个单独的线程;而NIO则是使用单线程或者只使用少量的多线程,每个连接共用一个线程。
NIO的最重要的地方是当一个连接创建后,不需要对应一个线程,这个连接会被注册到多路复用器上面,所以所有的连接只需要一个线程就可以搞定,当这个线程中的多路复用器进行轮询的时候,发现连接上有请求的话,才开启一个线程进行处理,也就是一个请求一个线程模式。
在NIO的处理方式中,当一个请求来的话,开启线程进行处理,可能会等待后端应用的资源(JDBC连接等),其实这个线程就被阻塞了,当并发上来的话,还是会有BIO一样的问题。
HTTP/1.1出现后,有了Http长连接,这样除了超时和指明特定关闭的http header外,这个链接是一直打开的状态的,这样在NIO处理中可以进一步的进化,在后端资源中可以实现资源池或者队列,当请求来的话,开启的线程把请求和请求数据传送给后端资源池或者队列里面就返回,并且在全局的地方保持住这个现场(哪个连接的哪个请求等),这样前面的线程还是可以去接受其他的请求,而后端的应用的处理只需要执行队列里面的就可以了,这样请求处理和后端应用是异步的.当后端处理完,到全局地方得到现场,产生响应,这个就实现了异步处理。
三、AIO
与NIO不同,当进行读写操作时,只须直接调用API的read或write方法即可。这两种方法均为异步的,对于读操作而言,当有流可读取时,操作系统会将可读的流传入read方法的缓冲区,并通知应用程序;对于写操作而言,当操作系统将write方法传递的流写入完毕时,操作系统主动通知应用程序。 即可以理解为,read/write方法都是异步的,完成后会主动调用回调函数。 在JDK1.7中,这部分内容被称作NIO.2,主要在Java.nio.channels包下增加了下面四个异步通道:
其中的read/write方法,会返回一个带回调函数的对象,当执行完读取/写入操作后,直接调用回调函数。
BIO是一个连接一个线程。
NIO是一个请求一个线程。
AIO是一个有效请求一个线程。
先来个例子理解一下概念,以银行取款为例:
Java对BIO、NIO、AIO的支持:
BIO、NIO、AIO适用场景分析:
另外,I/O属于底层操作,需要操作系统支持,并发也需要操作系统的支持,所以性能方面不同操作系统差异会比较明显。
在高性能的I/O设计中,有两个比较著名的模式Reactor和Proactor模式,其中Reactor模式用于同步I/O,而Proactor运用于异步I/O操作。
在比较这两个模式之前,我们首先的搞明白几个概念,什么是阻塞和非阻塞,什么是同步和异步,同步和异步是针对应用程序和内核的交互而言的,同步指的是用户进程触发IO操作并等待或者轮询的去查看IO操作是否就绪,而异步是指用户进程触发IO操作以后便开始做自己的事情,而当IO操作已经完成的时候会得到IO完成的通知。而阻塞和非阻塞是针对于进程在访问数据的时候,根据IO操作的就绪状态来采取的不同方式,说白了是一种读取或者写入操作函数的实现方式,阻塞方式下读取或者写入函数将一直等待,而非阻塞方式下,读取或者写入函数会立即返回一个状态值。
一般来说I/O模型可以分为:同步阻塞,同步非阻塞,异步阻塞,异步非阻塞IO
同步阻塞IO:在此种方式下,用户进程在发起一个IO操作以后,必须等待IO操作的完成,只有当真正完成了IO操作以后,用户进程才能运行。JAVA传统的IO模型属于此种方式!
同步非阻塞IO:在此种方式下,用户进程发起一个IO操作以后边可返回做其它事情,但是用户进程需要时不时的询问IO操作是否就绪,这就要求用户进程不停的去询问,从而引入不必要的CPU资源浪费。其中目前JAVA的NIO就属于同步非阻塞IO。
异步阻塞IO:此种方式下是指应用发起一个IO操作以后,不等待内核IO操作的完成,等内核完成IO操作以后会通知应用程序,这其实就是同步和异步最关键的区别,同步必须等待或者主动的去询问IO是否完成,那么为什么说是阻塞的呢?因为此时是通过select系统调用来完成的,而select函数本身的实现方式是阻塞的,而采用select函数有个好处就是它可以同时监听多个文件句柄,从而提高系统的并发性!
异步非阻塞IO:在此种模式下,用户进程只需要发起一个IO操作然后立即返回,等IO操作真正的完成以后,应用程序会得到IO操作完成的通知,此时用户进程只需要对数据进行处理就好了,不需要进行实际的IO读写操作,因为真正的IO读取或者写入操作已经由内核完成了。目前Java中还没有支持此种IO模型。
本文转自:云栖社区云
【摘要】对Java 9的炒作将不再局限于模块化(modularity),Java 9正在搜罗大量额外的功能模块,这些功能模块正作为Java增强提案(JEP)提交,并在OpenJDK (Java SE的参考实现项目)中实现。 在这篇文章中,我们将重点关注一些或将在Java 9整个生命周期中,对开发者的工作生…
对Java 9的炒作将不再局限于模块化(modularity),Java 9正在搜罗大量额外的功能模块,这些功能模块正作为Java增强提案(JEP)提交,并在OpenJDK (Java SE的参考实现项目)中实现。
在这篇文章中,我们将重点关注一些或将在Java 9整个生命周期中,对开发者的工作生活影响最大的JEP,包括新的HTTP/2支持和JShell REPL(读取-求值-打印-循环),后者带来了基于shell的交互式Java开发环境和探索性开发API。
HTTP/2
HTTP/2标准是HTTP协议的最新版本。当前版本HTTP/1.1始于1999年,存在着非常严重的问题,包括:
对头阻塞
在HTTP/1.1中,响应接收的顺序和请求发送的顺序相同。这意味着,例如,当查看一个包含许多小图像的大HTML页面时,图像资源将不得不在HTML页面资源之后排队,在浏览器完全加载完HTML页面之前,图像资源无法被发送。这就是“对头阻塞”,会导致许多潜在的页面渲染问题。
在HTTP/2中,响应数据可以按块(chunk)传输,甚至可以交叉传输,因此真正实现了请求和响应的多路复用。
一个站点的连接数限制
在HTTP/1.1标准中有这样的描述:“一个单用户的客户端不能与任何服务器保持2个以上的连接”。这个限制和对头阻塞问题一起,严重限制了页面的性能。
HTTP/2打破这种限制并认为连接是持久的,只有当用户跳转后或者发生技术性故障事件时,连接才会关闭。对多路复用的使用将有助于降低页面性能瓶颈。
HTTP控制头的开销
当前的HTTP版本使用简单的、基于文本的HTTP头信息来控制通信。这样做的优点是非常简单且易于理解,调试也很简单,只需通过连接指定端口并输入一些文本。然而,使用基于文本的协议会让小的响应包不成比例地膨胀。此外,大量的HTTP响应几乎没有或者根本没有有效负载(比如,HEAD请求只是要确定资源是否发生变化)。为实际上只包含最后修改时间的响应,使用完全基于文本的头信息(大约有700个字节,在HTTP1.1中,它们不能被压缩,尽管很容易做到)是当前HTTP标准中,不可思议的浪费。
另一个思路是对HTTP头信息使用二进制编码。这种方式能够极大地提高较小请求的速度且占用的网络带宽非常小。这正是HTTP/2已经选择的方法,虽然以协议精神制定标准应该选择基于文本的协议,但是二进制的效率有令人信服的理由,让我们这样做。
HTTP/2带来的期望
HTTP/2标准是由IETF HTTP工作组创建的,该组织由来自Mozilla、Google、 Microsoft、Apple,以及其他公司的代表和工程师组成,由来自CDN领军公司Akamai的高级工程师Mark Nottingham任主 席。因此,HTTP/2是一个为优化大型、高流量的网站而生的版本,它在实现简单、易于调试的基础上,确保了性能和网络带宽消耗。
该组织主 席总结了一些HTTP/2的关键属性:
相同的HTTP API成本更低的请求网络和服务器端友好缓存推送思维革命更多加密方式
带给Java的意义
自从1.0版本开始,Java就支持HTTP,但是多数代码出自完全不同的时代。例如,Java对HTTP的支持是围绕相对协议无关的框架(URL类)设计的,因此在网站成为主导地位的90年代,这种实现显得很不清晰。
Java对HTTP的支持是基于当时最好的设计思想,但是时过境迁,最重要的是Java对HTTP原始的支持出来时,HTTPS还没有出现。因此,Java的API将HTTPS作为一种移花接木,导致了不能简化的复杂性。
在现代社会,HTTPS开始变得无所不在,让HTTP日渐成为落后的技术。甚至,美国政府现在都通过了完全迁到HTTPS-only的计划。
JDK内核对HTTP的支持已经无法跟上现实网络的发展步伐。实际上,甚至JDK8也只不过是交付了一个支持HTTP/1.0的客户端,然而,大多数的开发者早已转而使用第三方客户端库了,比如Apache的HttpComponents。
所有这一切意味着,对HTTP/2的支持将是Java未来十年的核心功能。这也让我们重新审视我们的固有思维,重新写一套API并提供重新来过的机会。HTTP/2将是未来数年内,每位开发者主要面对的API。
新的API不再坚持协议中立性,使开发者可以完全抛弃过去的使用方式。这套API只关注HTTP协议,但是要进一步理解的是HTTP/2并没有从根本上改变原有的语义。因此,这套API是HTTP协议独立的,同时提供了对新协议中帧和连接处理的支持。
在新的API中,一个简单的HTTP请求,可以这样创建和处理:
HttpResponse response = HttpRequest .create(new URI(“http://www.infoq.com”)) .body(noBody()) .GET().send(); int responseCode = response.responseCode(); String responseBody = response.body(asString()); System.out.println(responseBody);
这种符合流畅风格/建造者模式(fluent/builder)的API,与现存的遗留系统相比,对开发者来说,更具现代感和舒适感。
虽然当前的代码库只支持HTTP/1.1,但是已经包含了新的API。这使得在对HTTP/2支持完成对过程中,开发者可以实验性地使用和验证新的API。
相关代码已经进入OpenJDK沙箱仓库中,并很快登陆JDK 9的主干。到那个时候,新的API将开始自动构建到Oracle的二进制beta版本中。现在,对HTTP/2的支持已经可用,并将在未来数月内最终完成。
在此期间,你可以使用Mercurial迁出源代码,并根据AdoptOpenJDK构建指导编译你迁出地代码,这样你就可以实验性地使用新的API了。
第一批完成的功能之一是当前版本力不能及的异步API。这个功能让长期运行的请求,可以通过sendAsync()方法,切换到VM管理的后台线程中:
HttpRequest req = HttpRequest .create(new URI(“http://www.infoq.com”)) .body(noBody()) .GET(); CompletableFuture<HttpResponse> aResp = req.sendAsync(); Thread.sleep(10); if (!aResp.isDone()) { aResp.cancel(true); System.out.println(“Failed to reply quickly…”); return; } HttpResponse response = aResp.get();
相比HTTP/1.1的实现,新的API带给开发者最多的是方便性,因为HTTP/1.1没有提供对已经发送到服务器端的请求的取消机制,而HTTP/2可以让客户端向已经被服务器端处理的请求,发送取消命令。
JShell
很多语言都为探索性开发提供了交互式环境。在某些情况下(特别是Clojure和其他Lisp方言),交互式环境占据了开发者的大部分编码时间,甚至是全部。其他语言,比如Scala或者JRuby也广泛使用REPL。
当然,此前Java曾经推出过Beanshell脚本语言,但是它没有实现完全标准化,而且近年来,该项目已经处于不活跃状态。在Java 8(以及jjs REPL)中引入的Nashorn Java实现打开了更广泛地考虑REPL并将交互式开发成为可能的大门。
一项努力将现代REPL引入Java 9的工作,以JEP 222作为开始,收录在OpenJDK的Kulla项目中。Kulla这个名字来自古巴比伦神话,是建造之神。该项目的主旨是提供最近距离的“完整Java”体验。该项目没有引入新的非Java语义,并禁用了Java语言中对交互式开发没有用处的语义(比如上层的访问控制修改或同步的语义)。
与所有REPL一样,JShell提供了命令行,而不是类似IDE的体验。语句和表达式能够在执行状态上下文中,被立即求值,而不是非得打包到类中。方法也是自由浮动的,而不必属于某个特定的类。相反,JShell使用代码片断“snippets”来提供上层执行环境。
与HTTP/2 API相似,JShell已经在独立的项目开发,以免在快速发展的时期影响主干构建的稳定性。JShell预计在2015年8月期间合并到主干。
现在,开发者可以参考AdoptOpenJDK说明指导,从头构建Kulla(源代码可以从Mercurial地址获得)。
对于一些上手实验,最简单的可能是使用一个独立的试验jar。这些jar包是社区专为不想从头构建的开发者构建好的。
这些试验jar包可以从AdoptOpenJDK CloudBees的CI构建实例中获得。
要使用它们,你需要安装Java 9 beta版(或者OpenJDK 9的构建版本)。然后下载jar文件,重命名为kulla.jar,然后在命令行输入如下:
$ java -jar kulla.jar | Welcome to JShell — Version 0.610 | Type /help for help ->
这是REPL的标准界面,和往常一样,命令是从单个字符开始并最终发出的。
JShell有一个相当完整(但仍在发展)的帮助语法,可以通过如下命令轻松获得:
-> /help Type a Java language expression, statement, or declaration. Or type one of the following commands: /l or /list [all] — list the source you have typed /seteditor <executable> — set the external editor command to use /e or /edit <name or id> — edit a source entry referenced by name or id /d or /drop <name or id> — delete a source entry referenced by name or id /s or /save [all|history] <file> — save the source you have typed /o or /open <file> — open a file as source input /v or /vars — list the declared variables and their values /m or /methods — list the declared methods and their signatures /c or /classes — list the declared classes /x or /exit — exit the REPL /r or /reset — reset everything in the REPL /f or /feedback <level> — feedback information: off, concise, normal, verbose, default, or ? /p or /prompt — toggle display of a prompt /cp or /classpath <path> — add a path to the classpath /h or /history — history of what you have typed /setstart <file> — read file and set as the new start-up definitions /savestart <file> — save the default start-up definitions to the file /? or /help — this help message /! — re-run last snippet /<n> — re-run n-th snippet /-<n> — re-run n-th previous snippet Supported shortcuts include: — show possible completions for the current text Shift- — for current method or constructor invocation, show a synopsis of the method/constructor
JShell支持TAB键自动补全, 因此我们可以很容易找到println()或者其他我们想使用的方法:
-> System.out.print print( printf( println(
传统的表达式求值也很容易,但是相比其他动态类型语言,Java的静态类型特征会更严格一点。JShell会自动创建临时变量来保存表达式的值,并确保它们保持在上下文域内供以后使用:
-> 3 * (4 + 5) | Expression value is: 27 | assigned to temporary variable $1 of type int -> System.out.println($1); 27
我们还可以使用/list命令,查看到目前为止输入的所有源代码:
-> /list 9 : 3 * (4 + 5) 10 : System.out.println($1);
使用/vars命令显示所有的变量(包括显式定义的和临时的),以及他们当前持有的值:
-> String s = “Dydh da” | Added variable s of type String with initial value “Dydh da” -> /vars | int $1 = 27 | String s = “Dydh da”
除了支持简单的代码行,REPL还允许非常简单地创建类和其它用户定义的类型。例如,可以用如下短短一行来创建类(请注意,开始和结束括号是必需的):
-> class Pet {} | Added class Pet -> class Cat extends Pet {} | Added class Cat
JShell代码非常简洁、自由浮动的性质意味着我们可以非常简单地使用REPL来演示Java语言的功能。例如,让我们来看看著名的类型问题,即Java数组的协变问题:
-> Pet[] pets = new Pet[1] | Added variable pets of type Pet[] with initial value [LPet;@2be94b0f -> Cat[] cats = new Cat[1] | Added variable cats of type Cat[] with initial value [LCat;@3ac42916 -> pets = cats | Variable pets has been assigned the value [LCat;@3ac42916 -> pets[0] = new Pet() | java.lang.ArrayStoreException thrown: REPL.$REPL13$Pet | at (#20:1)
这样的功能使JShell成为一种伟大的教学或研究工具,而且最接近Scala REPL的体验。使用/classpath切换,可以加载额外的jar包,从而可以在REPL直接使用互动式探索性API。
参与
主要的IDE已开始提供支持JDK 9早期版本的构建——包括Netbeans和Eclipse Mars。[urlhttps://www.jetbrains.com/idea/download/?spm=5176.blog26632.yqblogcon1.14.tXFWPP=””]IntelliJ 14.1[/url]据称支持JDK9,但目前还不清楚对新的模块化JDK扩展的支持力度。
到目前为止,这些IDE还不支持HTTP/2和JShell,因为这些功能还没有登陆OpenJDK的主干,但是开发者应该很期望它们能够早日出现在标准的JDK beta版本中,并且有IDE插件可以紧随其后。这些API仍在开发中,项目的领导者正在积极寻求最终用户的使用和参与。
The JDK 9 Outreach programme is also underway to encourage developers to test their code and applications on JDK 9 before it arrives. HTTP/2 & JShell aren’t the only new features being worked on – other new JDK 9 functionality under development as JEPs includes
JDK 9的宣传计划也正在鼓励开发者测试他们的代码并在JDK 9上运行应用程序。正在开发的新功能不止包括HTTP/2和JShell—— 其他作为JEP,JDK 9正在开发的新功能还包括:
102 Process API的更新(Process API Updates)165 编译器控制(Compiler Control)227 Unicode 7.0245 验证虚拟机代码行标记参数(Validate JVM Command-Line Flag Arguments)248: G1作为默认的垃圾回收器(Make G1 the Default Garbage Collector)TLS的一系列更新(TLS Updates) (JEP 219, 244, 249)
目前正在审议(以及考虑应该放在哪个Java版本)的所有JEP的完整列表可以在这里找到。