为什么我的++不是primefaces的?
为什么i++
在Java中不是primefaces的?
为了让Java更深入一些,我试着去计算线程循环执行的频率。
所以我用了一个
private static int total = 0;
在主类。
我有两个线程。
- 线程1:打印
System.out.println("Hello from Thread 1!");
- 线程2:打印
System.out.println("Hello from Thread 2!");
我计算由线程1和线程2打印的行。但线程1 +线程2的行数与打印的总行数不匹配。
这是我的代码:
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.logging.Level; import java.util.logging.Logger; public class Test { private static int total = 0; private static int countT1 = 0; private static int countT2 = 0; private boolean run = true; public Test() { ExecutorService newCachedThreadPool = Executors.newCachedThreadPool(); newCachedThreadPool.execute(t1); newCachedThreadPool.execute(t2); try { Thread.sleep(1000); } catch (InterruptedException ex) { Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex); } run = false; try { Thread.sleep(1000); } catch (InterruptedException ex) { Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex); } System.out.println((countT1 + countT2 + " == " + total)); } private Runnable t1 = new Runnable() { @Override public void run() { while (run) { total++; countT1++; System.out.println("Hello #" + countT1 + " from Thread 2! Total hello: " + total); } } }; private Runnable t2 = new Runnable() { @Override public void run() { while (run) { total++; countT2++; System.out.println("Hello #" + countT2 + " from Thread 2! Total hello: " + total); } } }; public static void main(String[] args) { new Test(); } }
i++
在Java中可能不是primefaces的,因为primefaces性是一个特殊的要求,在i++
的大部分使用中都不存在。 这个要求有一个很大的开销:增加primefaces操作的成本很高; 它涉及在软件和硬件两个层次的同步,不需要以普通的增量出现。
你可以说i++
应该被devise和logging为专门执行一个primefaces增量,所以使用i = i + 1
来执行非primefaces增量。 但是,这将打破Java,C和C ++之间的“文化兼容性”。 同样,它也会带走熟悉C语言的程序员理所当然的一种方便的表示法,赋予它一个特殊的含义,仅在有限的情况下才适用。
像for (i = 0; i < LIMIT; i++)
这样的基本C或C ++代码将转换成Java for (i = 0; i < LIMIT; i = i + 1)
。 因为使用primefacesi++
是不合适的。 更糟糕的是,从C或者其他类C语言到Java的程序员无论如何都会使用i++
,导致不必要地使用primefaces指令。
即使在机器指令集级别,由于性能原因,增量types操作通常也不是primefaces的。 在x86中,必须使用一个特殊的指令“locking前缀”来使inc
指令primefaces化:出于与上述相同的原因。 如果inc
始终是primefaces的,那么在需要非primefaces公司时决不会使用; 程序员和编译器会生成加载,加1和存储的代码,因为它会更快。
在一些指令集架构中,根本不存在primefacesinc
或可能没有inc
; 在MIPS上做一个primefaces公司,你必须编写一个使用ll
和sc
的软件循环:加载链接和存储条件。 加载链接读取的单词,并存储条件存储新的价值,如果单词没有改变,否则失败(这是检测到,并导致重试)。
i++
涉及两个操作:
- 读取我的当前值
- 递增该值并将其分配给
i
当两个线程同时在同一个variables上执行i++
时,它们都可以获得与i
相同的当前值,然后递增并将其设置为i+1
,因此您将获得单个增量而不是两个。
例如:
int i = 5; Thread 1 : i++; // reads value 5 Thread 2 : i++; // reads value 5 Thread 1 : // increments i to 6 Thread 2 : // increments i to 6 // i == 6 instead of 7
重要的是JLS(Java语言规范),而不是JVM的各种实现如何实现语言的某些特性。 JLS在第15.14.2节中定义了++ postfix运算符,它表示ia“将值1添加到variables的值并将总和存回variables”。 它没有提到或暗示multithreading或primefaces性。 对于这些JLS提供了易变和同步 。 此外,还有java.util.concurrent.atomic包(请参阅http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/package-summary.html )
为什么我在Java中不是primefaces的?
让我们把增量操作分解成多个语句:
线程1和2:
- 从内存中提取总值
- 将值加1
- 回写到内存
如果没有同步,那么假设线程1已经读取了值3并将其增加到4,但还没有写回。 此时,上下文切换发生。 线程2读取值3,递增它并发生上下文切换。 虽然两个线程都增加了总价值,但仍然是四种竞争条件。
i++
是一个简单的涉及3个操作的声明:
- 读取当前值
- 写新的价值
- 存储新的价值
这三个操作并不意味着一步执行,换句话说, i++
不是复合操作。 结果,当一个单一的非复合操作涉及多个线程时,各种事情都会出错。
作为一个例子想象这个场景:
时间1 :
Thread A fetches i Thread B fetches i
时间2 :
Thread A overwrites i with a new value say -foo- Thread B overwrites i with a new value say -bar- Thread B stores -bar- in i // At this time thread B seems to be more 'active'. Not only does it overwrite // its local copy of i but also makes it in time to store -bar- back to // 'main' memory (i)
时间3 :
Thread A attempts to store -foo- in memory effectively overwriting the -bar- value (in i) which was just stored by thread B in Time 2. Thread B has nothing to do here. Its work was done by Time 2. However it was all for nothing as -bar- was eventually overwritten by another thread.
在那里,你有它。 竞赛条件。
这就是为什么i++
不是primefaces的。 如果是这样,这一切都不会发生,每个fetch-update-store
都会自动发生。 这正是AtomicInteger
目的,在你的情况下,它可能适合。
PS
一本涵盖所有这些问题的优秀书籍,其中一些是: 实践中的Java并发
有两个步骤:
- 从内存中取我
- 将i + 1设置为i
所以这不是primefaces操作。 当thread1执行i ++,而thread2执行i ++时,我的最终值可能是i + 1。
在JVM中,增量涉及读取和写入,所以它不是primefaces的。
如果操作i++
是primefaces的,你就不会有机会从中读取数值。 这正是你想要使用i++
(而不是使用++i
)。
例如看下面的代码:
public static void main(final String[] args) { int i = 0; System.out.println(i++); }
在这种情况下,我们期望输出为: 0
(因为我们发布增量,例如第一次读取,然后更新)
这是操作不能primefaces化的原因之一,因为你需要读取值(并用它做些什么), 然后更新值。
另一个重要的原因是做一些primefaces的东西通常需要更多的时间,因为locking。 如果人们想要进行primefaces操作的话,那么在基本操作上的所有操作要花费一点点时间是很愚蠢的。 这就是为什么他们已经添加AtomicInteger
和其他primefaces类的语言。
并发性( Thread
类等)是Java v1.0中的一个附加function。 在此之前, i++
已经在testing版中添加了i++
,因此它在原始实现中(或多或less)的可能性更大。
程序员需要同步variables。 看看这个Oracle的教程 。
编辑:为了澄清,i ++是一个早于Java的定义良好的过程,因此Java的devise者决定保留该过程的原始function。
++运算符是在B(1969)中定义的,它早于java和线程。