Java Foundation (2) Self-increment and self-decrease and greedy rules


introduction

  The self-increment operator ++ and the self-decrement operator -- are provided in the JDK. Each of these two operators can be used in two ways: prefix (++a,--a) and postfix (a++,a--). Perhaps at this point, there are no readers who will complain that the prefix and suffix are quite simple. Isn't the prefix first +1 (or -1), and then use the value to participate in the operation, while the suffix is ​​the opposite. Is there a need for a long speech?
  The difference between suffix and suffix is ​​indeed the case, at least on the surface, but a deeper understanding is not so simple, and even seriously affects the correctness of your program. If you don't believe me, go see it next!


1. The real difference between prefix and suffix in java

  In Java, the operation is calculated from left to right, and according to the priority of the operator, the value of the expression is calculated one by one, and this value is used to participate in the operation of the next expression, such as: 1+2+3In fact, the 1+2expression is calculated first The value of 3, and then participate in the operation of the next expression (1+2)+3, ie 3+3. Another example is judgment if(a+2==3). And so on.
  a++is an expression, then a++there will be a calculation result of the expression, which is the old value of a (the value before adding 1). Conversely, ++athe value after adding 1 to the evaluation result of the expression. Therefore, the essential difference between the auto-increment prefix form and the postfix form is: the value of the expression (the result of the operation) is the value of the variable before adding 1 or the value of the variable after adding 1 (the same is true for auto-decrement) . It is not the difference between adding 1 first and adding 1 later , or in other words, the prefix and suffix forms are first added (minus 1) to get the value of the expression, and then participate in the next operation. Because this is an expression, the operation of the expression must be evaluated before the value of the expression can be obtained.

Let's take a look at a frequently asked question in an interview:

int a = 5;
a = a++;//What is the value of a at this time?

  Have you guessed the value of a? Let's analyze what we have learned above: a=a++it can be understood as the following steps:

1. Calculate a and add 1 to it, that isa=a+1

2. Calculate the value of the expression, because this is a suffix form, so a++the value of the expression is the value of a before adding 1 (the value is 5);

3. Assign the value of the expression to a, ie a=5.

So the final value of a is 5.

Similarly, if changed to a = ++a;, the value of a is 6. This is because ++athe value of the expression is a plus 1, which is 6.


2. Self-increment and self-decrement are two operations, not thread-safe

  The increment and decrement operators are not essentially one calculation operation, but two calculation operations. For a++example, this operation will be parsed by the compiler into: a=a+1, that is, it contains two unary operators (+, =), and the calculation operation of a unary operator can be regarded as an atomic operation. a++The steps can be described as:
  1. First add 1 to a, and store the result in the temporary space;
  2. Assign the result to a. So, the increment/decrement operator consists of two operations: an increment (decrement) operation and an assignment operation

  This principle seems useless to my programming, right? No, in a single-threaded environment, you can ignore this detail. But in the case of multithreading, you have to keep this detail in mind at all times. You must know that self-increment and self-decrement are not atomic operations, that is to say, they are not thread-safe operations. Therefore, under multi-threading, if you want to implement auto-increment and auto-decrement operations on shared variables, you need to add locks, or use the atomic auto-increment/decrement methods provided by the atomic operation classes provided by JDK (such as AtomincInteger, AtomicLongetc.).

Let's see an example to verify it. The following example provides three static variables (one is an atomic operation class), creates 10 threads, each thread adds 1 to these three variables in different ways, and loops 1000 times.

public class MyTest {
    static int a = 0;
    static int b = 0;
    //Atomic operation class
    static AtomicInteger atomicInt = new AtomicInteger(0);
    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 10; i++) {//Create 10 threads
            Thread t = new Thread() {
                @Override                public void run() {
                    for (int j = 0; j < 1000; j++) {//Calculate 1000 times
                        a = a + 1;
                        b++;
                        atomicInt.incrementAndGet();//Self-increment atomic method
                    }
                }
            };
            t.start();
        }
        // Determine whether the current active thread has only the main thread to ensure that the execution of 10 computing threads is completed.
        while (Thread.activeCount() > 1) {
            Thread.sleep(1000);
        }
        System.out.println("The result of a=a+1 under multi-threading is: " + a);
        System.out.println("The result of b++ under multi-threading is: " + b);
        System.out.println("The result of the atomic operation class AtomicInteger under multi-threading is: " + atomicInt.get());
    }
}

operation result:

The result of a=a+1 under multi-threading is: 8883 The result of
b++ under multi-threading is: 8974 The result of
atomic operation class AtomicInteger under multi-threading is: 10000

  As can be seen from the running results, a=a+1、b++it is not thread-safe, and the correct result 10000 is not calculated. That is, neither expression is atomic. In fact, they both contain two computational operations.


3. Thoughts caused by a+++b expressions

  Seeing this expression, it is really confusing: how does the compiler parse it, it is parsed into

a++ + b

still

a+ ++b

Really tangled, just run it directly on the compiler and see the result!

 int a = 5;
    int b = 5;
    int c=a+++b;
    System.out.println("The value of a is: "+a);
    System.out.println("The value of b is: "+b);

operation result:

The value of a is: 6
The value of b is: 5

  It can be confirmed from the results that a+++bit is actually parsed a++ +b, why should it be combined in this way? There are actually two reasons:

  • Operations in Java are performed from left to right;

  • The java compiler has a rule - the greedy rule. That is, the compiler will combine as many valid symbols as possible.

Then, a+++bthis combination can explain

But the union is: as many unions as possible, regardless of whether such unions are legal or not . Such as:

a--b

will be parsed by the compiler as

a-- b

Although this is illegal, the compiler still handles it in this way, which causes the compilation to fail and generates a compilation error.

Why do compilers use the greedy rule?

  From the above analysis, the greedy rule is not easy to use in programming. So, what is the main purpose of the greedy rule?
  The main purpose of the greedy rule is to analyze String strings, as you can see from the following example:

    String s = "\17";
    System.out.println("\\17 The value of the escape character is: "+s+" The length is: "+s.length());
    s = "\171";
    System.out.println("\\171 The value of the escape character is: "+s+" The length is: "+s.length());
    s = "\1717";
    System.out.println("\\1717 The value of the escape character is: "+s+" The length is: "+s.length());
    s = "\17178";
    System.out.println("\\17178 The value of the escape character is: "+s+" The length is: "+s.length());

operation result:

\17 The value of the escape character is: Length is: 1
\171 The value of the escape character is: y The length is: 1
\1717 The value of the escape character is: y7 The length is: 2
\17178 The value of the escape character is: y78 length is: 3

  "\17" is escaped to get a special character "". And "\171" also gets a character "y" after escaping. However, the strings obtained by "\1717" and "\17178" are greater than 1 and are no longer a character, but are "y7" and "y78" respectively.

  That is, the "\1717" string only escapes the "\171" part, and then links the "7" part. The "\17178" string only escapes the "\171" part, and then concatenates "78".

  Then why does "\171" not escape the "\17" part, and then link "1", but the escaped string as a whole?

  This is what the " greedy rule " dictates. The value range of the octal escape character is \0~\377. So when parsing the "\171" string, the compiler combines as many characters as possible into a transition character, "\171" is still within the range of values, so it is a character. However, the "\1718" string can combine at most the first 4 characters into a valid escape character "\171", and "\1717" has exceeded the value range and is not a valid character, so it is finally parsed as "\171" + "7" result. The same goes for "17178".

4. Summary

  • When the compiler analyzes characters, it will combine as many valid characters as possible, but there may be syntax errors.

  • The greedy rule is useful, especially the compiler's handling of escape characters.

Source: http://www.cnblogs.com/jinggod/p/8424808.html

If there is something inappropriate in the article, please correct me. You can also pay attention to my WeChat public account: `Learn java well` to get high-quality learning resources.




Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324813628&siteId=291194637