zero's Blog

持续迭代

更新值

在Java7时,对ConcurrentHashMap进行线程安全的更新操作需要使用循环来处理(可以参见http://blog.csdn.net/zero__007/article/details/49833819),但是在Java8中提供了更方便的原子更新方法。

1
2
3
public V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)

对于指定key做remappingFunction函数调用,remappingFunction函数返回值即为新的value,如果返回值为null,则从map中删除对应的key。compute返回key更新后的值(remappingFunction函数返回值)
1
2
3
4
public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction)

如果指定的key不存在,对该key做mappingFunction函数操作,mappingFunction函数返回值不为null,则将对应的k-v放到map中,否则不操作。如果key不存在computeIfAbsent返回值同mappingFunction,如果key存在返回key对应的value(此时mappingFunction不会调用)。

1
2
3
public V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)

类似与computeIfAbsent,仅对已经存在的key才计算新value。同样,如果remappingFunction返回值为null,会删除对应的k-v。
Read more »

[toc]

动态代理

  Java动态代理类位于Java.lang.reflect包下,一般主要涉及到以下两个类:
  InvocationHandler:该接口中仅定义了一个invoke方法。
  每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。

1
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;

  第一个参数obj一般是指要代理的类,method是被代理的方法,args为该方法的参数数组。这个抽象方法在代理类中动态实现。

  Proxy:该类的作用就是用来动态创建一个代理对象的类,其中主要包含以下方法:

1
2
3
4
5
6
7
//获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组.
public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)
throws IllegalArgumentException{…}

//返回代理类的一个实例,返回后的代理类可以当作被代理类使用.
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,
InvocationHandler h) throws IllegalArgumentException{…}

  loader: 一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载。
  interfaces: 一个Interface对象的数组,表示的是要给需要代理的对象提供一组什么接口,如果提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样就能调用这组接口中的方法了。
  h: 一个InvocationHandler对象,表示的是当这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上。

  Proxy.newProxyInstance (ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)做了以下几件事.
  (1)根据参数 loader 和 interfaces 调用 getProxyClass(loader, interfaces) 创建代理类$Proxy0. $Proxy0类实现了interfaces的接口,并继承了Proxy类.
  (2)实例化 $Proxy0 并在构造方法中把loader传过去, 接着 $Proxy0 调用父类Proxy的构造器,为h赋值。接着把得到的 $Proxy0 实例强制转换成代理类. 当执行代理类的方法时,就调用了 $Proxy0 类中的方法.在执行代理类方法中,调用父类Proxy中的h的invoke()方法.即InvocationHandler.invoke()。

Read more »

直接上代码:

1
2
3
4
5
public interface CrudRepository<T> {

Iterable<T> findAll();

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class BaseImpl <T> implements CrudRepository<T> {

@Override
public Iterable<T> findAll() {
Class<T> c = getTClass();
System.out.println(c);
common(c);
return null;
}

/**
* 通用的方法使用泛型的class去干一些事情。
*
* @param c 泛型类
*/
private void common(Class<T> c) {

}

/**
* 获取 T.class
*/
public Class<T> getTClass() {
return (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class PhoneRepository extends BaseImpl<Phone> {

public static class Phone {
private static final long serialVersionUID = 1L;
private String name;
private Long price;
private Long updateDate;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Long getPrice() {
return price;
}

public void setPrice(Long price) {
this.price = price;
}

public Long getUpdateDate() {
return updateDate;
}

public void setUpdateDate(Long updateDate) {
this.updateDate = updateDate;
}
}
}
1
2
3
4
5
6
7
8
9
10
11
public class Test {

public static void main(String[] args) {
PhoneRepository phoneRepository = new PhoneRepository();
Iterable<Phone> phones = phoneRepository.findAll();
}
}

结果:
class cn.zero.reflect.PhoneRepository$Phone



工具类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class GenericsUtils {

/**
* 通过反射,获得定义Class时声明的父类的范型参数的类型.
*
* 如public BookManager extends Manager<Book>
*/
public static Class getSuperClassGenericType(Class clazz) {
return getSuperClassGenericType(clazz, 0);
}

/**
* 通过反射,获得定义Class时声明的父类的范型参数的类型.
*
* 如public BookManager extends Manager<Book>, 返回Book
*/
public static Class getSuperClassGenericType(Class clazz, int index) throws IndexOutOfBoundsException {
Type genType = clazz.getGenericSuperclass();
if (!(genType instanceof ParameterizedType)) {
return Object.class;
}
Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
if (index >= params.length || index < 0) {
return Object.class;
}
if (!(params[index] instanceof Class)) {
return Object.class;
}
return (Class) params[index];
}
}

Process对象.waitFor()的阻塞问题

有时需要在程序中调用可执行程序或脚本命令:

1
2
Process process = Runtime.getRuntime().exec(shPath);
int exitCode = process.waitFor();

Runtime.getRuntime()返回当前应用程序的Runtime对象,该对象的exec()方法指示Java虚拟机创建一个子进程执行指定的可执行程序,并返回与该子进程对应的Process对象实例。通过Process可以控制该子进程的执行或获取该子进程的信息。

它的所有标准io(即stdin,stdout,stderr)操作都将通过三个流(getOutputStream(),getInputStream(),getErrorStream())重定向到父进程。父进程使用这些流来提供到子进程的输入和获得从子进程的输出。因为输入和输出流提供有限的缓冲区大小,如果读写子进程的输出流或输入流出现失败,当缓冲区满之后将无法继续写入数据,则可能导致子进程阻塞,最终造成阻塞在waifor()这里。

Read more »

[toc]

Strings

1
2
3
4
5
//padEnd 尾填充
String a = "12345";
String b = Strings.padEnd(a, 7, 'x');
System.out.println(b); // 12345xx

1
2
3
4
5
//padStart 头填充
String a="12345";
String b=Strings.padStart(a, 7, 'x');
System.out.println(b); // xx12345

1
2
3
4
5
//Strings.isNullOrEmpty  空校验
String a = "";
boolean b = Strings.isNullOrEmpty(a);
System.out.println(b);

1
2
3
4
5
//Strings.nullToEmpty  如果为null 转为""
String a = null;
String b = Strings.nullToEmpty(a);
System.out.println(b);

1
2
3
4
5
//Strings.emptyToNull  如果为"" 转为null
String a = "";
String b = Strings.emptyToNull(a);
System.out.println(b); // null

1
2
3
4
5
//Strings.repeat  重复字符串
String a = "123";
String b = Strings.repeat(a, 3);
System.out.println(b); // 123123123

1
2
3
4
5
6
//Strings.commonPrefix 获取a,b左公共部分字符串
String a = "abcdsfsfs";
String b = "abc3sfsd";
String c = Strings.commonPrefix(a, b);
System.out.println(c); // abc

1
2
3
4
5
6
//Strings.commonSuffix 获取a,b右公共部分字符串
String a = "faaxyz";
String b = "fwefxyz";
String c = Strings.commonSuffix(a, b);
System.out.println(c); // xyz

Read more »

之前使用到了Instrumentation来做字节码修改,用到了javaassist,顺便做个笔记,记录一下。

对于动态扩展现有类或接口的二进制字节码,有比较成熟的开源项目提供支持,如CGLib、ASM、Javassist等。其中,CGLib的底层基于ASM实现,是一个高效高性能的生成库;而ASM是一个轻量级的类库,但需要涉及到JVM的操作和指令;相比而言,Javassist要简单的多,完全是基于Java的API,但其性能相比前二者要差一些。

Read more »

转载自:https://blog.csdn.net/bohu83/article/details/126427401

有这样业务场景:对于一个主题subject,想看有哪些group订阅了。而对于消费者来说,订阅的时候就是需要支持前缀匹配的,比如XXX.order.pay,支持订阅XXX.order 就能获取消息。通常这就需要使用前缀匹配,可以使用Java库:

1
2
3
4
5
<dependency>
<groupId>com.googlecode.concurrent-trees</groupId>
<artifactId>concurrent-trees</artifactId>
<version>2.6.1</version>
</dependency>

使用的数据结构是基数树,而不是以为的字典树,基数树与字典树的区别在于基数树将单词压缩了, 节点变得更少,解决了字典树高度问题。

Read more »

堆外内存

​ 堆外内存是相对于堆内内存的一个概念。堆内内存是由JVM所管控的Java进程内存,我们平时在Java中创建的对象都处于堆内内存中,并且它们遵循JVM的内存管理机制,JVM会采用垃圾回收机制统一管理它们的内存。那么堆外内存就是存在于JVM管控之外的一块内存区域,因此它是不受JVM的管控。
​ DirectByteBuffer是通过虚引用(Phantom Reference)来实现堆外内存的释放的。
​ PhantomReference 是所有“弱引用”中最弱的引用类型。虚引用主要被用来跟踪对象被垃圾回收的状态,通过查看引用队列中是否包含对象所对应的虚引用来判断它是否即将被垃圾回收,从而采取行动。它并不被期待用来取得目标对象的引用,而目标对象被回收前,它的引用会被放入一个ReferenceQueue对象中,从而达到跟踪对象垃圾回收的作用。

Read more »

1.对象属性拷贝的常见方式及其性能

  在日常编码中,经常会遇到DO、DTO对象之间的转换,如果对象本身的属性比较少的时候,那么采用Hard Code工作量也不大,但如果对象的属性比较多的情况下,Hard Code效率就比较低。这时候就要使用其它工具类来进行对象属性的拷贝。
  常用的对象属性拷贝的方式和性能测试如下:

拷贝方式 copy次数1000 copy次数100000 copy次数1000000
Hard Code 1 ms 18 ms 43 ms
net.sf.cglib.beans.BeanCopier#copy 117 ms 107 ms 110 ms
org.springframework.beans.BeanUtils#copyProperties 137 ms 246 ms 895 ms
org.apache.commons.beanutils.PropertyUtils#copyProperties 212 ms 601 ms 7869 ms
org.apache.commons.beanutils.BeanUtils#copyProperties 275 ms 1732 ms 12380 ms

  结论:采用Hard Code方式进行对象属性Copy性能最佳;采用net.sf.cglib.beans.BeanCopier#copy方式进行对象属性copy性能最稳定;而org.apache.commons.beanutils.BeanUtils.copyProperties 方式在数据量大时性能下降最厉害。所以在日常编程中遇到具有较多属性的对象进行属性复制时优先考虑采用net.sf.cglib.beans.BeanCopier#copy

  以上的数据之所以产生巨大差距的原因在于其实现原理与方式的不同而导致的,Hard Code直接调用getter & setter方法值,cglib采用的是字节码技术,而后三种均采用反射的方式。前两者性能优异众所周知,但为何同样采用反射的方式进行属性Copy时产生的差异如此巨大呢?

Read more »

​ 网站为了支撑更大的用户访问量,往往需要对用户访问的数据做cache,服务机群和负载均衡来专门处理缓存,负载均衡的算法很多,轮循算法、哈希算法、最少连接算法、响应速度算法等,hash算法是比较常用的一种,它的常用思想是先计算出一个hash值,然后使用 CRC余数算法将hash值和机器数mod后取余数,机器的编号可以是0到N-1(N是机器数),计算出的结果一一对应即可。

​ 缓存最关键的就是命中率这个因素,如果命中率非常低,那么缓存也就失去了它的意义。如采用一般的CRC取余的hash算法虽然能达到负载均衡的目的,但是它存在一个严重的问题,那就是如果其中一台服务器down掉,那么就需要在计算缓存过程中将这台服务器去掉,即N台服务器,目前就只有N-1台提供缓存服务,此时需要一个rehash过程,而reash得到的结果将导致正常的用户请求不能找到原来缓存数据的正确机器,其他N-1台服务器上的缓存数据将大量失效,此时所有的用户请求全部会集中到数据库上,严重可能导致整个生产环境挂掉.

Read more »
0%