为什么我的++不是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公司,你必须编写一个使用llsc的软件循环:加载链接和存储条件。 加载链接读取的单词,并存储条件存储新的价值,如果单词没有改变,否则失败(这是检测到,并导致重试)。

i++涉及两个操作:

  1. 读取我的当前值
  2. 递增该值并将其分配给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. 从内存中提取总值
  2. 将值加1
  3. 回写到内存

如果没有同步,那么假设线程1已经读取了值3并将其增加到4,但还没有写回。 此时,上下文切换发生。 线程2读取值3,递增它并发生上下文切换。 虽然两个线程都增加了总价值,但仍然是四种竞争条件。

i++是一个简单的涉及3个操作的声明:

  1. 读取当前值
  2. 写新的价值
  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并发

有两个步骤:

  1. 从内存中取我
  2. 将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和线程。