zero's Blog

持续迭代

http://blog.csdn.net/zero__007/article/details/78288448 中简单介绍了一下ThreadLocal,每个ThreadLocal实例都有一个唯一的threadLocalHashCode初始值,在ThreadLocalMap中设置或获取Entry时,会根据threadLocalHashCode&(len-1)的值去对应的槽中操作。

而ThreadLocal解决Hash 冲突使用线性探测的方法,当一个线程对应多个ThreadLocal实例的场景中,在命中的情况下基本上一次hash就可以找到位置,如果发生没有命中的情况,则会引发性能会急剧下降,本身是O(1),结果变成了O(n)当在读写操作频繁的场景,这点导致性能的后滞。

Read more »

​ 早在JDK 1.2的版本中就提供Java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序。

​ ThreadLocal并不是一个Thread,而是Thread的局部变量,也许把它命名为ThreadLocalVariable更容易让人理解一些。当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。从线程的角度看,目标变量就象是线程的本地变量,这也是类名中“Local”所要表达的意思。

Read more »

​ ThreadLocal这个类给线程提供了一个本地变量,这个变量是该线程自己拥有的。在该线程存活和ThreadLocal实例能访问的时候,保存了对这个变量副本的引用.当线程消失的时候,所有的本地实例都会被GC。建议ThreadLocal最好是 private static 修饰的成员。

1
2
3
4
5
6
7
8
9
10
public class ThreadLocal<T> {
private final int threadLocalHashCode = nextHashCode();
private static AtomicInteger nextHashCode =
new AtomicInteger();
private static final int HASH_INCREMENT = 0x61c88647;

private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}

​ ThreadLocals 解决Hash 冲突使用线性探测的方法。其中key为ThreadLocal对象,因为Thread类中的threadLocals是个map对象,意味一个Thread中可以存放多个ThreadLocal对象。因此用threadLocalHashCode来区分是哪个ThreadLocal类型。

Read more »

​ 在使用了线程池(如Executor)的情况下,那么即使父线程已经结束,子线程依然存在并被池化。这样,线程池中的线程在下一次请求被执行的时候,ThreadLocal对象的get()方法返回的将不是当前线程中设定的变量,因为池中的“子线程”根本不是当前线程创建的,当前线程设定的 ThreadLocal变量也就无法传递给线程池中的线程。因此,必须将外部线程中的ThreadLocal变量显式地传递给线程池中的线程。

Read more »

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

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()这里。
  针对这种情况,解法是要清空getInputStream()getErrorStream()这两个流。而且两个流的清空一定是异步的。

1
2
3
4
5
6
7
8
9
10
11
static void drainInBackground(final InputStream is) {  
new Thread(new Runnable(){
public void run(){
try{
while( is.read() >= 0 );
} catch(IOException e){
// return on IOException
}
}
}).start();
}

  还有一种解法是用ProcessBuilder来创建Process对象,必须要使用ProcessBuilder的redirectErrorStream方法。redirectErrorStream方法设置为ture的时候,会将getInputStream()getErrorStream()两个流合并,自动会清空流,无需自己处理。如果是false,getInputStream()getErrorStream()两个流分开,就必须自己处理,程序如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
try {  
ProcessBuilder pbuilder=new ProcessBuilder("ping","192.168.0.125");
pbuilder.redirectErrorStream(true);
process=pbuilder.start();
reader=new BufferedReader(new InputStreamReader(process.getInputStream()));
String line=null;
while((line=reader.readLine())!=null){
System.out.println(line);
}
int result=process.waitFor();
System.out.println(result);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
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
Process process = null;
try {
process = launchProcess(cmdlist, environment);

StringBuilder sb = new StringBuilder();
String output = getOutput(process.getInputStream());
String errorOutput = getOutput(process.getErrorStream());
sb.append(output);
sb.append("\n");
sb.append(errorOutput);

boolean ret = process.waitFor(1L, TimeUnit.SECONDS);
if (!ret) {
System.out.println(command + " is terminated abnormally. ret={}, str={}" + ret + " " + sb.toString());
}
return sb.toString();
} catch (Throwable e) {
System.out.println("Failed to run " + command + ", ");
e.printStackTrace();
} finally {
if (null != process) {
process.destroy();
}
}

return "";

注意process一定要释放

https://blog.csdn.net/lijingyao8206/article/details/46430403
https://blog.csdn.net/lijingyao8206/article/details/46483815
https://blog.csdn.net/lijingyao8206/article/details/46715409

ASM是一个提供字节码解析和操作的框架。可以用于解析和生成字节码。

解析
要解析的类

1
2
3
4
5
6
7
public class Programmer {

public void code() {
System.out.println("I'm a Programmer,Just Coding.....");
}

}

ClassPrintVisitor 类继承自ClassVisitor类来打印解析类的类名,父类名以及“is”开头的属性和方法。

Read more »

Map是映射表,基本思想是键-值(对)关联,可以使用键来查找值。一般常见的Map有HashMap,LinkedHashMap,TreeMap,WeakHashMap,ConcurrentHashMap,IdentityHashMap。

  • HashMap不多说,最常见,非线程安全的。

  • LinkedHashMap取得“键值对”的顺序是其插入次序,或者是最近最少使用(LRU)的次序。只比HashMap慢一点;而在迭代访问时反应更快,因为它使用的链表维护内部次序。

  • TreeMap基于红黑树的实现。查看“键”或“键值对”时,它们会被排序(次序由Comparable或者Comparator决定)。

  • WeakHashMap弱键(weak key)映射,允许释放映射所指向的对象。如果映射之外没有引用指向某个“键”,则此“键”对应的值可以被垃圾回收器回收。

  • ConcurrentHashMap一种线程安全的Map。

  • IdentityHashMap使用==代替equals()对“键”进行比较的散列映射。意思是key在逻辑上可以相等,但是物理地址不能相等。

Read more »

看下面一段代码:

1
2
3
4
5
6
7
8
9
// public final class Integer extends Number
Number num = new Integer(1); //ok
List<Number> list = new ArrayList<>(); //ok
list.add(new Integer(3)); //ok

ArrayList<Number> list = new ArrayList<Integer>(); //type mismatch
List<? extends Number> list = new ArrayList<Number>();
list.add(new Integer(1)); //error
list.add(new Float(1.2f)); //error

为什么Number的对象可以由Integer实例化,而ArrayList<Number>的对象却不能由ArrayList<Integer>实例化?list中的<? extends Number>声明其元素是Number或Number的派生类,为什么不能add Integer 或 float?

为了解决这些问题,需要了解Java中的逆变和协变以及泛型中通配符用法。

Read more »

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Supplier;

public class Builder<T> {
private final Supplier<T> instantiator;
private List<Consumer<T>> setterList = new ArrayList<>();

private Builder(Supplier<T> instantiator) {
this.instantiator = instantiator;
}

public static <T> Builder<T> of(Supplier<T> instantiator) {
return new Builder<>(instantiator);
}

public <P> Builder<T> with(IOneParamSetter<T, P> setter, P p) {
Consumer<T> c = instance -> setter.accept(instance, p);
setterList.add(c);
return this;
}

public <P0, P1> Builder<T> with(ITwoParamSetter<T, P0, P1> setter, P0 p0, P1 p1) {
Consumer<T> c = instance -> setter.accept(instance, p0, p1);
setterList.add(c);
return this;
}

public T build() {
T value = instantiator.get();
setterList.forEach(setter -> setter.accept(value));
setterList.clear();
return value;
}

@FunctionalInterface
public interface IOneParamSetter<T, P> {
void accept(T t, P p);
}

@FunctionalInterface
public interface ITwoParamSetter<T, P0, P1> {
void accept(T t, P0 p0, P1 p1);
}

}
Read more »

转载自:https://mp.weixin.qq.com/s/LTKKoE_QhxX8ov_IyiMJgA

项目中使用HttpClient 调用其它服务,经常出现Address already in use (Bind failed)的问题。很明显是一个端口绑定冲突的问题,于是大概排查了一下当前系统的网络连接情况和端口使用情况,发现是有大量time_wait的连接一直占用着端口没释放,导致端口被占满(最高的时候6w+个),因此HttpClient建立连接的时候会出现申请端口冲突的情况。

于是为了解决time_wait的问题,认为可以通过连接池来保存tcp连接,减少HttpClient在并发情况下随机打开的端口数量,复用原来有效的连接。但是新的问题也由连接池的设置引入了。

在估算连接池最大连接数的时候,参考了业务高峰期时的请求量为1分钟1.2w pv,接口平响为1.3s,因此qps为120001.3\60=260。然后通过观察了业务日志,每次连接建立耗时1.1s左右, 再留70%+的上浮空间(怕连接数设置小出系统故障),最大连接数估计为2601.1*1.7约等于500

Read more »
0%