9.4Java 代码优化技巧及原理
1.合理并谨慎使用多线程
使用场景:(I/O阻塞,多CPU并发,多用户并发)
资源争用与同步问题
java.util.concurrent
启动线程数=【任务执行时间/(任务执行时间-IO等待时间)】*CPU内核数
最佳启动线程数和CPU内核数据成正比,和IO阻塞时间成反比。如果任务都是CPU计算型任务,那么线程数量最多不超过CPU内核数,因为启动再多线程,CPU也来不及调度;
相反如果任务需要等待磁盘操作,网络响应,那么多启动线程有助于提高任务并发度,提高系统吞吐能力,改善系统性能。
2.竞态条件与临界区
在同一程序中运行多个线程本身不会导致问题,问题在于多个线程访问了相同的资源。
当两个线程竞争同一资源时,如果对资源的访问顺序敏感,就称存在竞态条件。
导致竞态条件发生的代码区称作临界区。
在临界区中使用适当的同步就可以避免竞态条件。
3.Java线程安全
允许被多个线程安全执行的代码称作线程安全的代码。
方法局部变量:
局部变量存储在线程自己的栈中。也就是说,局部变量永远也不会被多个线程共享。所以,基础类型的局部变量是线程安全的。
方法局部的对象引用:
如果在某个方法中创建的对象不会逃逸出该方法,那么它就是线程安全的。(不与其他线程共享,仅在线程自己的栈帧中,线程自己访问,栈帧出栈,)
对象成员变量:
对象成员存储在堆上,如果两个线程同时更新同一个对象的同一个成员,那这个代码就不是线程安全的。
Tomcat多线程编程环境,编码时,大脑中应该有多线程模型。
4.ThreadLocal
![](https://static001.geekbang.org/infoq/f0/f0cc6439be73c81cdaf778bd9d57be74.png)
创建一个ThreadLocal变量(X类静态成员变量):
public static ThreadLocal myThreadLocal = new ThreadLocal();
存储此对象的值(A类a方法):
X.myThreadLocal.set("A thread local value");
读取一个ThreadLocal对象的值(B类b方法):
String threadLocalValue=(String)X.myThreadLocal.get();
关键点:ThreadLocal.set源码
public void set(T value){
Thread t=Thread.currentThread();
ThreadLocalMap map=getMap(t);
if(map!=null){
map.set(this,value);
}else{
createMap(t,value);
}
}
void createMap(Thread t,T firstValued){
t.threadLocals=new ThreadLocalMap(this,firstValue);
}
5.Java内存泄漏
Java内存泄漏是由于开发人员的错误引起的。
如果程序保留对永远不再使用的对象的引用,这些对象将会占用并耗尽内存。
长生命周期对象
静态容器
缓存
合理使用线程池和对象池:
复用线程或对象资源,避免在程序的生命周期中创建和删除大量对象
池管理算法(记录哪些对象时空闲的,哪些对象正在使用)
对象内容清除(ThreadLocal的清空)
使用合适的JDK容器类(顺序表,链表,Hash)
LinkList和ArrayList的区别和使用场景(查询多?增删多?)
HashMap的算法实现及应用场景
使用concurrent包,ConcurrentHashMap和HashMap的线程安全特性有什么不同?(HashMap:线程不安全)(ConcurrentHashMap:分段锁:分段并发--保证数据安全)
缩短对象的生命周期,加速垃圾回收:
减少对象驻留内存的时间
在使用时创建对象,用完释放
创建对象的步骤(静态代码段-静态成员变量-父类构造函数-子类构造函数)
public class StaticClass{
static {
A a=new A();
}
public static void cc(){
System.out.println("cc()");
}
}
使用I/O buffer及NIO:
延迟写与提前读策略
异步无阻塞IO通信
使用组合替代继承:
减少对象耦合
避免太深的继承层次带来的对象创建性能损失
合理使用单例模式:
无状态对象
线程安全
评论