用多长时间代替int可以防弹以下方法-有效的Java
考虑以下摘自Joshua Bloch的代码-高效Java,第263页用多长时间代替int可以防弹以下方法-有效的Java,java,Java,考虑以下摘自Joshua Bloch的代码-高效Java,第263页 // Broken - requires synchronization! private static volatile int nextSerialNumber = 0; public static int generateSerialNumber() { return nextSerialNumber++; } 解决问题的一种方法 generateSerialNumber方法用于添加 将同步修改器添加到其 宣言
// Broken - requires synchronization!
private static volatile int nextSerialNumber = 0;
public static int generateSerialNumber() {
return nextSerialNumber++;
}
解决问题的一种方法
generateSerialNumber方法用于添加
将同步修改器添加到其
宣言。这确保
不需要多次调用
交错,并且每次调用
将看到所有以前的
调用。一旦你做到了,
您可以而且应该移除挥发性物质
来自nextSerialNumber的修饰符。到
防弹的方法,使用时间长
而不是int,或引发异常
如果nextSerialNumber即将换行
同步后删除volatile
,因为它是冗余的。但是,这有什么害处吗如果我同时拥有同步和易失性(如
这仅仅意味着long将容纳比int多得多的数字 或者在nextSerialNumber即将换行时引发异常 这意味着这里的问题是,您的数字用完了,最终导致溢出。您希望确保不会发生这种情况。问题是,如果你是一个可能的最大整数,并且你递增,程序不会失败。它不会递增,但结果不再正确 使用long将推迟这种可能性。抛出异常将表明它已发生 用long代替int意味着什么
它确保序列号不会在很长时间内滚动。使用
int
可能会用尽所有可用值(因此nextSerialNumber
将具有最大可能的int值),然后在下一个增量时,该值会自动滚动到最小(负)int值,这几乎肯定不是您从序列号中所期望的:-)防弹的一种方法是(除上述方法外)
如果(nextSerialNumber>=整数.MAX_值)
//抛出异常
或者打印出一些内容,或者在调用例程时捕获该异常。在多线程上下文中,volatile/AtomicInteger比synchronized更快。在单线程微基准测试中,它们基本相同。部分原因是synchronized是一个操作系统调用,而volatile完全是用户空间 我从Java6Update23上的以下程序中得到这个输出
Average time to synchronized++ 10000000 times. was 110368 us
Average time to synchronized on the class ++ 10000000 times. was 37140 us
Average time to volatile++ 10000000 times. was 19660 us
我无法解释为什么类上的同步比普通对象快
代码:
静态最终对象o=新对象();
静态int num=0;
静态最终AtomicInteger num2=新的AtomicInteger();
公共静态void main(字符串…参数)抛出InterruptedException{
最终整数运行=10*1000*1000;
perfTest(新的Runnable(){
公开募捐{
for(int i=0;i
long
是64位整数格式,而int
是32位。这些整数类型通常在没有任何警告的情况下溢出-但是溢出long
比溢出int
要花费2^32倍的迭代次数@Jean homial,噢。我认为穿线是防弹的。明白!作者将其添加到一个主要关于合成的段落中,这确实有点让人困惑。是的,同时使用volatile和synchronized可能会对性能造成影响。volatile的性能影响明显小于synchronized。(它也做得更少)谢谢。您是否有关于“如果我同时使用同步和易失性”的答案?应该注意的是,20亿个序列号太多了。如果你每秒产生一个,你就有超过60年的价值。使用long
而不是int
,即使以每秒一百万次的燃烧速度,你也有足够的时间来覆盖近30万年。即使燃烧速度达到1G/s(祝你好运,很快就能达到这个目标!),你的寿命仍然是目前认为的人类最长寿命的两倍多。让我们在这里有一些观点!事实上,我在一个已有10年历史的遗留应用程序的日志表中看到过这种情况。相信我,你不想成为那个午夜支持电话的人。顺便说一下,我们通过将计数器重置为1来“修复”它。旧数据早就被存档了。@Peter:公平地说,我通常只是随机输入UUID。尽管它很胖,但它的优点是可以跨系统合并ID。我偶尔喜欢暂停一下,感谢我工作在一个我们很少需要担心保存ID的时代
Average time to synchronized++ 10000000 times. was 110368 us
Average time to synchronized on the class ++ 10000000 times. was 37140 us
Average time to volatile++ 10000000 times. was 19660 us
static final Object o = new Object();
static int num = 0;
static final AtomicInteger num2 = new AtomicInteger();
public static void main(String... args) throws InterruptedException {
final int runs = 10 * 1000 * 1000;
perfTest(new Runnable() {
public void run() {
for (int i = 0; i < runs; i++)
synchronized (o) {
num++;
}
}
public String toString() {
return "synchronized++ " + runs + " times.";
}
}, 4);
perfTest(new Runnable() {
public void run() {
for (int i = 0; i < runs; i++)
synchronized (Main.class) {
num++;
}
}
public String toString() {
return "synchronized on the class ++ " + runs + " times.";
}
}, 4);
perfTest(new Runnable() {
public void run() {
for (int i = 0; i < runs; i++)
num2.incrementAndGet();
}
public String toString() {
return "volatile++ " + runs + " times.";
}
}, 4);
}
public static void perfTest(Runnable r, int times) throws InterruptedException {
ExecutorService es = Executors.newFixedThreadPool(times);
long start = System.nanoTime();
for (int i = 0; i < times; i++)
es.submit(r);
es.shutdown();
es.awaitTermination(1, TimeUnit.MINUTES);
long time = System.nanoTime() - start;
System.out.println("Average time to " + r + " was " + time / times / 10000 + " us");
}