January 2022 Java Teaching Course 63-Atomicity

Posted by 2gd-2be-2rue on Wed, 12 Jan 2022 18:10:52 +0100

I. Atomicity

1.volatile-Problem

Code Analysis:

package com.itheima.myvolatile;

public class Demo {
    public static void main(String[] args) {
        MyThread1 t1 = new MyThread1();
        t1.setName("Road Classmates");
        t1.start();

        MyThread2 t2 = new MyThread2();
        t2.setName("Xiaopi classmates");
        t2.start();
    }
}
package com.itheima.myvolatile;

public class Money {
    public static int money = 100000;
}
package com.itheima.myvolatile;

public class MyThread1 extends  Thread {
    @Override
    public void run() {
        while(Money.money == 100000){

        }

        System.out.println("Marriage fund is no longer 100,000");
    }
}

package com.itheima.myvolatile;

public class MyThread2 extends Thread {
    @Override
    public void run() {
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        Money.money = 90000;
    }
}

Procedural issues: Although a girl knows that the marriage fund is 100,000, when the balance of the fund changes, she cannot know the latest balance.

2.volatile Solution

Problems in the above cases:

When thread A modifies shared data, thread B does not get the latest value in time. If the original value is still being used, problems will occur

1, heap memory is unique, each thread has its own thread stack.

2. When each thread uses variables in the heap, it first copies them into a copy of the variables.

3. Each use in the thread is taken from a copy of the variable.

Volatile keyword: Force threads to look at the latest values for the shared area each time they are used

Code implementation: Solve using volatile keyword

package com.itheima.myvolatile;

public class Demo {
    public static void main(String[] args) {
        MyThread1 t1 = new MyThread1();
        t1.setName("Road Classmates");
        t1.start();

        MyThread2 t2 = new MyThread2();
        t2.setName("Xiaopi classmates");
        t2.start();
    }
}
package com.itheima.myvolatile;

public class Money {
    public static volatile int money = 100000;
}
package com.itheima.myvolatile;

public class MyThread1 extends  Thread {
    @Override
    public void run() {
        while(Money.money == 100000){

        }

        System.out.println("Marriage fund is no longer 100,000");
    }
}

package com.itheima.myvolatile;

public class MyThread2 extends Thread {
    @Override
    public void run() {
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        Money.money = 90000;
    }
}

3.synchronized Solution

synchronized Solution:

1. Thread acquires lock

2. Empty copy of variable

3. Copy the latest value of a shared variable into a copy of the variable

4. Execute Code

5. Assign values in the modified copy of the variable to shared data

6. Release lock

Code implementation:

package com.itheima.myvolatile2;

public class Demo {
    public static void main(String[] args) {
        MyThread1 t1 = new MyThread1();
        t1.setName("Road Classmates");
        t1.start();

        MyThread2 t2 = new MyThread2();
        t2.setName("Xiaopi classmates");
        t2.start();
    }
}
package com.itheima.myvolatile2;

public class Money {
    public static Object lock = new Object();
    public static volatile int money = 100000;
}
package com.itheima.myvolatile2;

public class MyThread1 extends  Thread {
    @Override
    public void run() {
        while(true){
            synchronized (Money.lock){
                if(Money.money != 100000){
                    System.out.println("Marriage fund is no longer 100,000");
                    break;
                }
            }
        }
    }
}
package com.itheima.myvolatile2;

public class MyThread2 extends Thread {
    @Override
    public void run() {
        synchronized (Money.lock) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            Money.money = 90000;
        }
    }
}

4. Atomicity

Summary: Atomicity means that in one or more operations, all operations are performed without interruption by any factor, or all operations are not performed, and multiple operations are an indivisible whole.

Code implementation:

package com.itheima.threadatom;

public class AtomDemo {
    public static void main(String[] args) {
        MyAtomThread atom = new MyAtomThread();

        for (int i = 0; i < 100; i++) {
            new Thread(atom).start();
        }
    }
}
class MyAtomThread implements Runnable {
    private volatile int count = 0; //Number of ice cream deliveries

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            //1, read data from shared data into this thread stack.
            //2, modify the value of the copy of the variable in this thread stack
            //3, the value of the copy of the variable in this thread stack is assigned to the shared data.
            count++;
            System.out.println("Already sent" + count + "Ice cream");
        }
    }
}

Code summary: count++ is not an atomic operation and may be interrupted by other threads during execution

5. The volatile keyword does not guarantee atomicity

Solution: We can add locks to count++ operations, so count++ operations are the code in the critical zone, which can only be executed by one thread at a time, so count++ becomes an atomic operation.

package com.itheima.threadatom2;

public class AtomDemo {
    public static void main(String[] args) {
        MyAtomThread atom = new MyAtomThread();

        for (int i = 0; i < 100; i++) {
            new Thread(atom).start();
        }
    }
}
class MyAtomThread implements Runnable {
    private volatile int count = 0; //Number of ice cream deliveries
    private Object lock = new Object();

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            //1, read data from shared data into this thread stack.
            //2, modify the value of the copy of the variable in this thread stack
            //3, the value of the copy of the variable in this thread stack is assigned to the shared data.
            synchronized (lock) {
                count++;
                System.out.println("Already sent" + count + "Ice cream");
            }
        }
    }
}

6. Atomicity_ Atomic Integer

Overview: Java from JDK1.5 Beginning with java. Util. Concurrent. The atomic package, or Atomic Package, provides a simple, efficient, and thread-safe way to update a variable. Because of change

There are many types of quantities, so a total of 13 classes are provided in the Atomic package, belonging to four types of atomic renewal methods: the basic type of atomic renewal, the array of atomic renewal, the reference to atomic renewal, and the attribute (field) of atomic renewal. This time we will only explain

The Atomic package provides the following three classes:

AtomicBoolean: Atomic Update Boolean Type

AtomicInteger: Atomic Update Integer

AtomicLong: Atomic Update Long Integer

The methods provided by these three classes are almost identical, so this section will only cover AtomicInteger as an example, and the common methods used by AtomicInteger are as follows:

public AtomicInteger(): 	   			 Initialize an atomic type with a default value of 0 Integer
public AtomicInteger(int initialValue):  Initializes an atomic type of a specified value Integer

int get():   			 				 Get value
int getAndIncrement():      			 Add the current value to 1 atomically, noting that the value returned here is the value before the increase.
int incrementAndGet():     				 Add the current value to 1 atomically, noting that this returns an incremented value.
int addAndGet(int data):				 The values that will be entered and the values in the example will be atomically ( AtomicInteger In value)Adds and returns the result.
int getAndSet(int value):   			 Set to atomically newValue And returns the old value.

Code implementation:

package com.itheima.threadatom3;

import java.util.concurrent.atomic.AtomicInteger;

public class MyAtomIntergerDemo1 {
//    public AtomicInteger(): 	                Initialize an atomic Integer with a default value of 0
//    Public Atomic Integer (int initialValue): Initializes an atomic Integer of a specified value
    public static void main(String[] args) {
        AtomicInteger ac = new AtomicInteger();
        System.out.println(ac);

        AtomicInteger ac2 = new AtomicInteger(10);
        System.out.println(ac2);
    }

}
package com.itheima.threadatom3;

import java.lang.reflect.Field;
import java.util.concurrent.atomic.AtomicInteger;

public class MyAtomIntergerDemo2 {
//    int get(): 		 		 Get value
//    int getAndIncrement(): Atomically add the current value to 1, note that the value returned here is the value before the increase.
//    int incrementAndGet(): Atomically add the current value to 1, note that the incremented value is returned here.
//    int addAndGet(int data): 	  Adds the parameter atomically to the value in the object and returns the result.
//    int getAndSet(int value): Atomically set to the value of newValue and return the old value.
    public static void main(String[] args) {
//        AtomicInteger ac1 = new AtomicInteger(10);
//        System.out.println(ac1.get());

//        AtomicInteger ac2 = new AtomicInteger(10);
//        int andIncrement = ac2.getAndIncrement();
//        System.out.println(andIncrement);
//        System.out.println(ac2.get());

//        AtomicInteger ac3 = new AtomicInteger(10);
//        int i = ac3.incrementAndGet();
//        System.out.println(i);// Self-increasing value
//        System.out.println(ac3.get());

//        AtomicInteger ac4 = new AtomicInteger(10);
//        int i = ac4.addAndGet(20);
//        System.out.println(i);
//        System.out.println(ac4.get());

        AtomicInteger ac5 = new AtomicInteger(100);
        int andSet = ac5.getAndSet(20);
        System.out.println(andSet);
        System.out.println(ac5.get());
    }
}

7.AtomicInteger-Memory Resolution

AtomicInteger principle: spin lock + CAS algorithm

CAS algorithm:

There are three operands (memory value V, old expected value A, value B to modify)

When the old expected value A ==memory value is modified successfully, change V to B

When the old expected value A!= Memory value modification failed at this time, do nothing

And retrieve the latest value now (the retrieved action is spin)

8.AtomicInteger-Source Parsing

Code implementation:

package com.itheima.threadatom4;

public class AtomDemo {
    public static void main(String[] args) {
        MyAtomThread atom = new MyAtomThread();

        for (int i = 0; i < 100; i++) {
            new Thread(atom).start();
        }
    }
}
package com.itheima.threadatom4;

import java.util.concurrent.atomic.AtomicInteger;

public class MyAtomThread implements Runnable {
    //private volatile int count = 0; // Number of ice cream deliveries
    //private Object lock = new Object();
    AtomicInteger ac = new AtomicInteger(0);

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            //1, read data from shared data into this thread stack.
            //2, modify the value of the copy of the variable in this thread stack
            //3, the value of the copy of the variable in this thread stack is assigned to the shared data.
            //synchronized (lock) {
//                count++;
//                ac++;
            int count = ac.incrementAndGet();
            System.out.println("Already sent" + count + "Ice cream");
           // }
        }
    }
}

Source Parsing:

//First increase, then get the result
public final int incrementAndGet() {
        //+ 1 results since
        //this represents the current atomicInteger
        //1 Add once
        return U.getAndAddInt(this, VALUE, 1) + 1;
}

public final int getAndAddInt(Object o, long offset, int delta) {
        //Old v Value
        int v;
        //Spin process
        do {
            //Keep getting old values
            v = getIntVolatile(o, offset);
            //If this method returns false, continue spinning
            //If this method returns true, the spin ends
            //o denotes memory value
            //Old v Value
            //v + delta modified value
        } while (!weakCompareAndSetInt(o, offset, v, v + delta));
            //Role: Compare the values in memory, whether the old values are equal, if equal, write the modified values into memory, return true. Indicates that the modification was successful.
            //                                 If not, the modified value cannot be written to memory, returning false. Indicates that the modification failed.
            //If the modification fails, continue spinning.
        return v;
}

9. Pessimistic Lock and Optimistic Lock

The difference between synchronized and CAS:

**Same point: ** Shared data can be secured in multi-threaded scenarios.

**The difference: **synchronized always looks at the worst possible scenario and assumes that someone else has the potential to modify the data every time it is retrieved. Therefore, locks are locked before each operation shares data. (pessimistic lock)

cas is optimistic, assuming that no one else will modify the data each time they get it, so they will not be locked. Only when you modify shared data, you check to see if someone else has modified it.

If someone else has modified it, I will get the latest value again.

If no one else has modified it, I will now modify the value of the shared data directly. (Optimistic lock)

Topics: Java Back-end