idea 配置fiddler代理http请求
目前遇到一个需求,需要用fiddler抓取idea插件的http请求。
解决办法,在idea64.exe.vmoptions和idea.exe.vmoptions加入下面的配置
-DproxySet=true
-Dhttp.proxyHost=127.0.0.1
-Dhttp.proxyPort=8888
目前遇到一个需求,需要用fiddler抓取idea插件的http请求。
解决办法,在idea64.exe.vmoptions和idea.exe.vmoptions加入下面的配置
-DproxySet=true
-Dhttp.proxyHost=127.0.0.1
-Dhttp.proxyPort=8888
最近看java 代码优化,总结下java 代码常见调优策略
1.优化代码
2.优化设计
3.优化算法
4.时间换空间
5.空间换时间
6.参数调优
总结:调优策略只是其中一环,上线前我们要做性能基准测试(benchmark)--->自下而上排查性能问题--->确定调优策略(此文种的6种)--->除了调优,还要兜底,因为再怎么调,我们系统也是有极限的,那么兜底策略有限流熔断,还有扩容(根据流量实时扩容,或者预测性提前扩容,比如秒杀活动时,提前扩容。)
我这个在idea中编译,发现需要依赖第三方包
在右边ant编译,右键----properties---external classpath----加入依赖的jar,就好了。
导致可见性的原因是缓存,导致有序性的原因是编译优化,那解决可见性、有序性最直接的办法就是禁用缓存和编译优化,但是这样问题虽然解决了,我们程序的性能可就堪忧了。合理的方案应该是按需禁用缓存以及编译优化。java 内存模型是个很复杂的规范,可以从不同的视角来解读,站在我们这些程序员的视角,本质上可以理解为,Java 内存模型规范了 JVM 如何提供按需禁用缓存和编译优化的方法。具体来说,这些方法包括 volatile、synchronized 和 final 三个关键字,以及六项 Happens-Before 规则,这也正是本期的重点内容
一。volatile使用---volatile 关键字并不是 Java 语言的特产,古老的 C 语言里也有,它最原始的意义就是禁用 CPU 缓存。,我们声明一个 volatile 变量 volatile int x = 0,它表达的是:告诉编译器,对这个变量的读写,不能使用 CPU 缓存,必须从内存中读取或者写入。这个语义看上去相当明确,但是在实际使用的时候却会带来困惑。
如下面的代码:直觉上看,应该是 42,那实际应该是多少呢?这个要看 Java 的版本,如果在低于 1.5 版本上运行,x 可能是 42,也有可能是 0;如果在 1.5 以上的版本上运行,x 就是等于 42。
// 以下代码来源于【参考 1】
class VolatileExample {
int x = 0;
volatile boolean v = false;
public void writer() {
x = 42;
v = true;
}
public void reader() {
if (v == true) {
// 这里 x 会是多少呢?
}
}
}
Happens-Before 六规则,happends before 可以理解为:前面一个操作的结果对后续操作是可见的
1.程序的顺序性规则: 这条规则是指在一个线程中,按照程序顺序,前面的操作 Happens-Before 于后续的任意操作
2.volatile 变量规则(java 1.5之后),指对一个 volatile 变量的写操作, Happens-Before 于后续对这个 volatile 变量的读操作
3.传递性: 如果 A Happens-Before B,且 B Happens-Before C,那么 A Happens-Before C。
分析: “x=42” Happens-Before 写变量 “v=true” ,这是规则 1 的内容;
写变量“v=true” Happens-Before 读变量 “v=true”,这是规则 2 的内容 。
再根据这个传递性规则,我们得到结果:“x=42” Happens-Before 读变量“v=true”。
管程是一种通用的同步原语,在 Java 中指的就是 synchronized,synchronized 是 Java 里对管程的实现。
synchronized (this) { // 此处自动加锁
// x 是共享变量, 初始值 =10
if (this.x < 12) {
this.x = 12;
}
} // 此处自动解锁
管程中锁的规则,可以这样理解:假设 x 的初始值是 10,线程 A 执行完代码块后 x 的值会变成 12(执行完自动释放锁),线程 B 进入代码块时,能够看到线程 A 对 x 的写操作,也就是线程 B 能够看到 x==12。这个也是符合我们直觉的,应该不难理解。
5.线程 start() 规则:它是指主线程 A 启动子线程 B 后,子线程 B 能够看到主线程在启动子线程 B 前的操作。
换句话说就是,如果线程 A 调用线程 B 的 start() 方法(即在线程 A 中启动线程 B),那么该 start() 操作 Happens-Before 于线程 B 中的任意操作。具体可参考下面示例代码。
Thread B = new Thread(()->{
// 主线程调用 B.start() 之前
// 所有对共享变量的修改,此处皆可见
// 此例中,var==77
});
// 此处对共享变量 var 修改
var = 77;
// 主线程启动子线程
B.start();
换句话说就是,如果在线程 A 中,调用线程 B 的 join() 并成功返回,那么线程 B 中的任意操作 Happens-Before 于该 join() 操作的返回。具体可参考下面示例代码。
Thread B = new Thread(()->{
// 此处对共享变量 var 修改
var = 66;
});
// 例如此处对共享变量修改,
// 则这个修改结果对线程 B 可见
// 主线程启动子线程
B.start();
B.join()
// 子线程所有对共享变量的修改
// 在主线程调用 B.join() 之后皆可见
// 此例中,var==66
最后说下final---volatile 为的是禁用缓存以及编译优化,我们再从另外一个方面来看,有没有办法告诉编译器优化得更好一点呢?这个可以有,就是final 关键字。
final 修饰变量时,初衷是告诉编译器:这个变量生而不变,可以可劲儿优化。这也是函数式编程快的原因,不受各种happens before约束
Java 编译器在 1.5 以前的版本的确优化得很努力,以至于都优化错了。
这些年,我们的 CPU、内存、I/O 设备都在不断迭代,不断朝着更快的方向努力。但是,在这个快速发展的过程中,有一个核心矛盾一直存在,就是这三者的速度差异。为了合理利用 CPU 的高性能,平衡这三者的速度差异,计算机体系机构、操作系统、编译程序都做出了贡献,主要体现为:1.CPU 增加了缓存,以均衡与内存的速度差异;2.操作系统增加了进程、线程,以分时复用 CPU,进而均衡 CPU 与 I/O 设备的速度差异 3. 编译程序优化指令执行次序,使得缓存能够得到更加合理地利用
一:缓存导致的可见性问题,同一个cpu的缓存可见,但是不同cpu的缓存不那么可见了。这是硬件程序员给软件程序员挖的坑
二:线程切换带来的原子性问题
三:编译优化带来的有序性问题
public class Singleton {
static Singleton instance;
static Singleton getInstance(){
if (instance == null) {
synchronized(Singleton.class) {
if (instance == null)
instance = new Singleton();
}
}
return instance;
}
}
这看上去一切都很完美,无懈可击,但实际上这个 getInstance() 方法并不完美。问题出在哪里呢?出在 new 操作上,我们以为的 new 操作应该是:
分配一块内存 M;
在内存 M 上初始化 Singleton 对象;
然后 M 的地址赋值给 instance 变量。
但是实际上优化后的执行路径却是这样的:
分配一块内存 M;
将 M 的地址赋值给 instance 变量;
最后在内存 M 上初始化 Singleton 对象。
优化后会导致什么问题呢?我们假设线程 A 先执行 getInstance() 方法,当执行完指令 2 时恰好发生了线程切换,切换到了线程 B 上;如果此时线程 B 也执行 getInstance() 方法,那么线程 B 在执行第一个判断时会发现 instance != null ,所以直接返回 instance,而此时的 instance 是没有初始化过的,如果我们这个时候访问 instance 的成员变量就可能触发空指针异常。
如果对instance进行volatile语义声明,就可以禁止指令重排序,避免该情况发生。