JUC concurrent programming (crazy God)

Keywords: Java

JUC concurrent programming

  1. What is JUC
    JUC is the class package under java.util.concurrent, which is specially used for multithreading development.

2. Threads and processes

Process is the application program in the operating system and the basic unit of resource allocation. Thread is used to perform specific tasks and functions. It is the smallest unit of CPU scheduling and allocation
A process can often contain multiple threads, at least one

1) Process
A program, QQ.EXE Music.EXE; Data + code + pcb
A process can contain multiple threads, including at least one thread!
How many threads does Java have by default? 2 threads! main thread, GC thread
2) Thread
Open a process Typora, write, wait a few minutes, and it will be automatically saved (the thread is responsible for it)
For Java, Thread, Runable and Callable are used to start threads.

put questions to? Can JAVA really start threads? I can't drive!
Java does not have permission to start threads and operate hardware. This is a native local method that calls the underlying C + + code.

public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }
	//This is a C + + bottom layer. Java has no permission to operate the bottom hardware
    private native void start0();

3) Concurrent
Multiple threads operate on the same resource.

The CPU has only one core and simulates multiple threads. The world's martial arts are only fast. Then we can use CPU fast alternation to simulate multithreading.
The essence of concurrent programming: make full use of CPU resources!
4) Parallel
Parallel: multiple people walking together

The CPU is multi-core, and multiple threads can execute at the same time. We can use thread pool!

Gets the number of cores of the cpu

public class Test1 {
    public static void main(String[] args) {
        //Gets the number of cores of the cpu
        System.out.println(Runtime.getRuntime().availableProcessors());
    }
}

5) Status of the thread

public enum State {
    	//function
        NEW,
    	//function
        RUNNABLE,
    	//block
        BLOCKED,
    	//wait for
        WAITING,
    	//Timeout wait
        TIMED_WAITING,
    	//termination
        TERMINATED;
    }

6)wait/sleep
1. From different classes
wait => Object
sleep => Thread

Generally, dormancy is used in Enterprises:

TimeUnit.DAYS.sleep(1); //Dormancy for 1 day
TimeUnit.SECONDS.sleep(1); //Sleep for 1s

2. About lock release
wait will release the lock;
sleep sleeps and won't release the lock;

3. The scope of use is different
wait must be in the synchronization code block;
Sleep can sleep anywhere;

4. Need to catch exceptions
wait is an exception that does not need to be caught;
sleep must catch exceptions;

3.Lock interface

1) Traditional synchronized

public class Demo01 {
    public static void main(String[] args) {
        final Ticket ticket = new Ticket();

        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        },"C").start();
    }
}
// Resource class OOP attributes and methods
class Ticket {
    private int number = 30;

    //How to sell tickets
    public synchronized void sale() {
        if (number > 0) {
            System.out.println(Thread.currentThread().getName() + "Sold the third" + (number--) + "Ticket remaining" + number + "Ticket");
        }
    }
}

2)Lock
Both synchronized and lock are reentrant locks
Reentrant lock, also known as recursive lock (similar to unlocking the door, you no longer need to unlock when entering the bedroom), you can enter other locks inside

//Reentrant lock
public class SyncLockDemo {

    public synchronized void add() {
        add();
    }

    public static void main(String[] args) {
        //Lock demonstrates reentrant locks
        Lock lock = new ReentrantLock();
        //Create thread
        new Thread(()->{
            try {
                //Lock
                lock.lock();
                System.out.println(Thread.currentThread().getName()+" Outer layer");
                try {
                    //Lock
                    lock.lock();
                  System.out.println(Thread.currentThread().getName()+" Inner layer");
                }finally {
                    //Release lock
                    lock.unlock();
                }
            }finally {
                //Release do
                lock.unlock();
            }
        },"t1").start();

        //Create a new thread
        new Thread(()->{
            lock.lock();
            System.out.println("aaaa");
            lock.unlock();
        },"aa").start();

       // new SyncLockDemo().add(); // Stack overflow, repeatable loop call
       // synchronized
//        Object o = new Object();
//        new Thread(()->{
//            synchronized(o) {
//                System.out.println(Thread.currentThread().getName() + "outer layer");
//                Synchronized (o) {/ / system. Out. Println (thread. Currentthread(). Getname() + "middle layer");
//                    synchronized (o) {
//      System.out.println(Thread.currentThread().getName() + "inner layer");
//                    }
//                }
//            }
//
//        },"t1").start();
    }
}

Lock lock

public class LockDemo {
    public static void main(String[] args) {
        final Ticket2 ticket = new Ticket2();

        new Thread(() -> {
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        }, "C").start();
    }
}
//lock Trilogy
//1,    Lock lock=new ReentrantLock();
//2. lock.lock() lock
//3. Finally = > unlock: lock.unlock();
class Ticket2 {
    private int number = 30;
	
    // Create lock
    Lock lock = new ReentrantLock();
    //How to sell tickets
    public synchronized void sale() {
        lock.lock(); // Open lock
        try {
            if (number > 0) {
                System.out.println(Thread.currentThread().getName() + "Sold the third" + (number--) + "Ticket remaining" + number + "Ticket");
            }
        }finally {
            lock.unlock(); // Close the lock
        }

    }
}

3) The difference between Synchronized and Lock
1. Synchronized built-in Java keyword, Lock is a Java class
2. Synchronized cannot judge the status of obtaining the Lock, but Lock can judge
3. Synchronized will automatically release the lock. Lock must be manually locked and released! Deadlock may be encountered
4. Synchronized thread 1 (obtain lock - > block) and thread 2 (wait); lock does not necessarily wait all the time. Lock will have a trylock to try to obtain the lock, which will not cause a long wait.
5. Synchronized is a re entrant Lock that cannot be interrupted and is not fair; Lock is a re entrant Lock that can judge the Lock and set its own fair Lock and non fair Lock;
6. Synchronized is suitable for locking a small number of code synchronization problems, and Lock is suitable for locking a large number of synchronization codes;

4. Relationship between producers and consumers

1) Synchronized version

public class ConsumeAndProduct {
    public static void main(String[] args) {
        Data data = new Data();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "AA").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "BB").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "CC").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "DD").start();
    }
}

class Data {
    private int num = 0;

    // +1
    public synchronized void increment() throws InterruptedException {
        // Judgment waiting
        if (num != 0) {
            this.wait();
        }
        num++;
        System.out.println(Thread.currentThread().getName() + "=>" + num);
        // Notify other threads that + 1 execution is complete
        this.notifyAll();
    }

    // -1
    public synchronized void decrement() throws InterruptedException {
        // Judgment waiting
        if (num == 0) {
            this.wait();
        }
        num--;
        System.out.println(Thread.currentThread().getName() + "=>" + num);
        // Notify other threads that - 1 execution is complete
        this.notifyAll();
    }
}

When there are too many threads, false wakeups may occur

1.AA got it, num+1
2.CC got it, wait
3.AA grabs and wait s to wake up other threads
4.CC grabs it again. The wait method has a feature: where to sleep, where to wake up, so if you don't go, execute num+1 directly

Solution: change if to while to prevent false wake-up

lock version

public class LockCAP {
    public static void main(String[] args) {
        Data2 data = new Data2();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "C").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "D").start();
    }
}

class Data2 {
    private int num = 0;
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    // +1
    public  void increment() throws InterruptedException {
        lock.lock();
        try {
            // Judgment waiting
            while (num != 0) {
                condition.await();
            }
            num++;
            System.out.println(Thread.currentThread().getName() + "=>" + num);
            // Notify other threads that + 1 execution is complete
            condition.signalAll();
        }finally {
            lock.unlock();
        }

    }

    // -1
    public  void decrement() throws InterruptedException {
        lock.lock();
        try {
            // Judgment waiting
            while (num == 0) {
                condition.await();
            }
            num--;
            System.out.println(Thread.currentThread().getName() + "=>" + num);
            // Notify other threads that + 1 execution is complete
            condition.signalAll();
        }finally {
            lock.unlock();
        }

    }
}

Advantages of Condition
Accurate notification and wake-up threads!

What if we want to specify the next order of notification? We can use Condition to specify the notification process~

public class ConditionDemo {
    public static void main(String[] args) {
        Data3 data3 = new Data3();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data3.printA();
            }
        },"A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data3.printB();
            }
        },"B").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data3.printC();
            }
        },"C").start();
    }

}
class Data3 {
    private Lock lock = new ReentrantLock();
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();
    private int num = 1; // 1A 2B 3C

    public void printA() {
        lock.lock();
        try {
            // Business code judgment - > execution - > notification
            while (num != 1) {
                condition1.await();
            }
            System.out.println(Thread.currentThread().getName() + "==> AAAA" );
            num = 2;
            condition2.signal();
        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public void printB() {
        lock.lock();
        try {
            // Business code judgment - > execution - > notification
            while (num != 2) {
                condition2.await();
            }
            System.out.println(Thread.currentThread().getName() + "==> BBBB" );
            num = 3;
            condition3.signal();
        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public void printC() {
        lock.lock();
        try {
            // Business code judgment - > execution - > notification
            while (num != 3) {
                condition3.await();
            }
            System.out.println(Thread.currentThread().getName() + "==> CCCC" );
            num = 1;
            condition1.signal();
        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}
A==> AAAA
B==> BBBB
C==> CCCC
A==> AAAA
B==> BBBB
C==> CCCC

5. Eight situations of synchrod lock

How to judge who the lock is! Who the lock is?
Locks lock: object, Class

  1. One class has two synchronization methods
public class dome01 {
    public static void main(String[] args) {
        Phone phone = new Phone();

        new Thread(() -> { phone.sendMs(); }).start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(() -> { phone.call(); }).start();
    }
}

class Phone {
    public synchronized void sendMs() {
        System.out.println("send message");
    }
    public synchronized void call() {
        System.out.println("phone");
    }
}
The output result is
 send message
 phone
  1. Delay texting by 4s
public class dome01 {
    public static void main(String[] args) throws InterruptedException {
        Phone phone = new Phone();

        new Thread(() -> {
            try {
                phone.sendMs();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(() -> { phone.call(); }).start();
    }
}

class Phone {
    public synchronized void sendMs() throws InterruptedException {
        TimeUnit.SECONDS.sleep(4);
        System.out.println("send message");
    }
    public synchronized void call() {
        System.out.println("phone");
    }
}
Or send text messages first and then call again!

Reason: it is not sequential execution, but synchronized. The locked object is a method call! For two methods, the same lock is used. Who gets it first, who executes it first, and the other waits

  1. Add a common method
public class dome01 {
    public static void main(String[] args) throws InterruptedException {
        Phone phone = new Phone();

        new Thread(() -> {
            try {
                phone.sendMs();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(() -> { phone.hello(); }).start();
    }
}

class Phone {
    public synchronized void sendMs() throws InterruptedException {
        TimeUnit.SECONDS.sleep(4);
        System.out.println("send message");
    }
    public synchronized void call() {
        System.out.println("phone");
    }
    public void hello() {
        System.out.println("hello");
    }
}
The output result is
hello
 send message

Reason: hello is a common method. It is not affected by the synchronized lock and does not have to wait for the lock to be released

  1. If we use two objects, one calling to send a text message and the other calling to make a phone call, what is the whole order?
public class dome01 {
    public static void main(String[] args) throws InterruptedException {
        Phone phone1 = new Phone();
        Phone phone2 = new Phone();

        new Thread(() -> {
            try {
                phone1.sendMs();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(() -> { phone2.call(); }).start();
    }
}

class Phone {
    public synchronized void sendMs() throws InterruptedException {
        TimeUnit.SECONDS.sleep(4);
        System.out.println("send message");
    }
    public synchronized void call() {
        System.out.println("phone");
    }
    public void hello() {
        System.out.println("hello");
    }
}
Output results
 phone
 send message

Reason: two objects have two locks, and there will be no waiting. I slept for 4s after texting, so I made a call first

  1. For the same object, two synchronized methods plus static are changed into static methods
    Text first, then call
  2. Or text first and then call

Why does the previous object always execute first when static is added? Why does it wait later?
The reason is: for static methods, there is only one copy for the whole Class. For different objects, the same method is used, which means that this method belongs to this Class. If static static methods use synchronized locking, the synchronized lock will lock the whole object! No matter how many objects, there is only one lock for static locks, Whoever gets the lock first will execute it first. Other processes need to wait!

7. What is the calling sequence of using a static synchronization method, a synchronization method and an object?

public class dome01 {
    public static void main(String[] args) throws InterruptedException {
        Phone phone1 = new Phone();
//        Phone phone2 = new Phone();

        new Thread(() -> {
            try {
                phone1.sendMs();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(() -> { phone1.call(); }).start();
    }
}

class Phone {
    public static synchronized void sendMs() throws InterruptedException {
        TimeUnit.SECONDS.sleep(4);
        System.out.println("send message");
    }
    public synchronized void call() {
        System.out.println("phone");
    }
    public void hello() {
        System.out.println("hello");
    }
}
Output results
 phone
 send message

Because a lock is a Class template and a lock is an object lock. So there is no waiting, run directly.

  1. What is the calling sequence of using a static synchronization method, a synchronization method and two objects?
public class dome01 {
    public static void main(String[] args) throws InterruptedException {
        Phone phone1 = new Phone();
        Phone phone2 = new Phone();

        new Thread(() -> {
            try {
                phone1.sendMs();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(() -> { phone2.call(); }).start();
    }
}

class Phone {
    public static synchronized void sendMs() throws InterruptedException {
        TimeUnit.SECONDS.sleep(4);
        System.out.println("send message");
    }
    public synchronized void call() {
        System.out.println("phone");
    }
    public void hello() {
        System.out.println("hello");
    }
}
Output results
 phone
 send message

Reason: the two locks are not the same thing

Summary:
this from new is the lock of a specific object
static Class is the only template lock

6. Unsafe assembly

1) List is not safe

   public class ListTest {
    public static void main(String[] args) {
  // Vector resolution
       List<String> list = new Vector<>();

        //Collections resolution
       List<String> list = Collections.synchronizedList(new ArrayList<>());

        // CopyOnWriteArrayList solution
      List<String> list = new CopyOnWriteArrayList<>();
  
        for (int i = 1; i <=10; i++) {
            new Thread(() -> {
                list.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(list);
            },String.valueOf(i)).start();
        }
    }
}

CopyOnWriteArrayList: copy on write! An optimization strategy in the field of COW computer programming

The core idea is that if multiple Callers require the same resource (such as memory or data storage on disk) at the same time, they will jointly obtain the same pointer to the same resource. The system will not really copy a private copy to the caller until a caller's view modifies the resource content, The original resources seen by other Callers remain unchanged. This process is transparent to other Callers. The main advantage of this method is that if the caller does not modify the resource, no private copy will be created. Therefore, multiple Callers can share the same resource only during the read operation.

There is no need to lock when reading. If multiple threads are adding data to the CopyOnWriteArrayList when reading, the old data will still be read, because the old CopyOnWriteArrayList will not be locked when writing.

When multiple threads call, list, read, fixed, write (overwrite operation exists); Avoid overwriting when writing, resulting in data disorder;

CopyOnWriteArrayList is better than Vector. Where is it?
The underlying Vector is implemented using the synchronized keyword: it is particularly inefficient.
CopyOnWriteArrayList uses Lock lock, which will be more efficient!

2) set is not safe
Set and List are the same: in the case of multithreading, an ordinary set is thread unsafe;

There are two solutions:
Set class wrapped with synchronized of Collections tool class
Write replicated JUC solutions using CopyOnWriteArraySet

public class SetTest {
    public static void main(String[] args) {
      
          1. Set<String> set = Collections.synchronizedSet(new HashSet<>());
          2. Set<String> set = new CopyOnWriteArraySet<>();
         
//        Set<String> set = new HashSet<>();
        Set<String> set = new CopyOnWriteArraySet<>();

        for (int i = 1; i <= 30; i++) {
            new Thread(() -> {
                set.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(set);
            },String.valueOf(i)).start();
        }
    }
}

3) Map is not secure

public class MapTest {
    public static void main(String[] args) {
        //Is map used like this? No, I don't use this at work
        //What is the default equivalent? new HashMap<>(16,0.75);
       
        //  Solution
          1. Map<String, String> map = Collections.synchronizedMap(new HashMap<>());
          2. Map<String, String> map = new ConcurrentHashMap<>();
        
        Map<String, String> map = new ConcurrentHashMap<>();
        //Load factor, initialization capacity
        for (int i = 1; i < 100; i++) {
            new Thread(()->{
                map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
                System.out.println(map);
            },String.valueOf(i)).start();
        }
    }
}

7. Callable

1. Can have return value;
2. Exceptions can be thrown;
3. Different methods, call()

Find a class that is related to both the Runnable interface and the Callable interface
The Runnable interface has an implementation class FutureTask
FutureTask constructs can pass Callable

//Compare two interfaces
//Implement Runnable interface
class MyThread1 implements Runnable {
    @Override
    public void run() {

    }
}

//Implement Callable interface
class MyThread2 implements Callable {

    @Override
    public Integer call() throws Exception {
        System.out.println(Thread.currentThread().getName()+" come in callable");
        return 200;
    }
}

public class Demo1 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //The Runnable interface creates a thread
        new Thread(new MyThread1(),"AA").start();

        //The Callable interface cannot be directly, and an error is reported
       // new Thread(new MyThread2(),"BB").start();

        //FutureTask
        FutureTask<Integer> futureTask1 = new FutureTask<>(new MyThread2());

        //lamda expression
        FutureTask<Integer> futureTask2 = new FutureTask<>(()->{
            System.out.println(Thread.currentThread().getName()+" come in callable");
            return 1024;
        });

        //Create a thread
        new Thread(futureTask2,"lucy").start();
        new Thread(futureTask1,"mary").start();
		//isDone end
//        while(!futureTask2.isDone()) {
//            System.out.println("wait.....");
//        }
        //Call the get method of FutureTask
        System.out.println(futureTask2.get());
        System.out.println(futureTask1.get());
        System.out.println(Thread.currentThread().getName()+" come over");
          //FutureTask principle future tasks
        /**
         * 1,The teacher is thirsty in class. It is inappropriate to buy a ticket. The lecture thread continues, and the main thread is not affected.
         *   Just start the thread, find the monitor of the class to buy water for me, buy the water back, and get it directly when necessary
         *
         * 2,4 Students, 1 student 1 + 2... 5, 2 student 10 + 11 + 12.... 50, 3 student 60 + 61 + 62, 4 student 100 + 200
         *      The second student has a large amount of calculation,
         *     FutureTask Start a single thread to calculate for 2 students, summarize 1, 3 and 4 first, and finally wait for 2 students to complete the calculation, and summarize uniformly
         *
         * 3,Test, do what you can do, and finally look at what you can't do
         *
         * Summary once
         *
         */
    }
}

8. Common auxiliary classes

1) CountDownLatch, decrease the count

public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        // The total is 6
        CountDownLatch countDownLatch = new CountDownLatch(6);

        for (int i = 1; i <= 6; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "==> Go Out");
                countDownLatch.countDown(); // Value - 1
            },String.valueOf(i)).start();
        }
        countDownLatch.await(); // Wait for the counter to zero and execute down
        System.out.println("close door");
    }
}

When there is no count

2) CyclickBarrier


public class CyclicBarrierDemo {
    public static void main(String[] args) {
        // Main thread
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7,() -> {
            System.out.println("Summon the Dragon");
        });

        for (int i = 1; i <= 7; i++) {
            // Child thread
            int finalI = i;
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "Collected the third" + finalI + "Dragon Ball");
                try {
                    cyclicBarrier.await(); // Add count wait
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

3) Semaphore, signal light
semaphore.acquire() obtains the resource. If the resource has been used up, wait for the resource to be released before using it!
semaphore.release() releases the current semaphore by + 1, and then wakes up the waiting thread!

Function: mutually exclusive use of multiple shared resources! Concurrent flow restriction, control the maximum number of threads!

//6 cars, 3 parking spaces
public class SemaphoreDemo {
    public static void main(String[] args) {
        //Create Semaphore and set the number of licenses
        Semaphore semaphore = new Semaphore(3);

        //Simulate 6 vehicles
        for (int i = 1; i <=6; i++) {
            new Thread(()->{
                try {
                    //seize
                    semaphore.acquire();

                    System.out.println(Thread.currentThread().getName()+" Grabbed the parking space");

                    //Set random stop time
                    TimeUnit.SECONDS.sleep(new Random().nextInt(5));

                 System.out.println(Thread.currentThread().getName()+" ------Left the parking space");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    //release
                    semaphore.release();
                }
            },String.valueOf(i)).start();
        }
    }
}

9. Read write lock

Without locking, multithreaded reading and writing will cause the problem of unreliable data.

public class ReadWriteLockDemo {
    public static void main(String[] args) {
        MyCache myCache = new MyCache();
        int num = 6;
        for (int i = 1; i <= num; i++) {
            int finalI = i;
            new Thread(() -> {

                myCache.write(String.valueOf(finalI), String.valueOf(finalI));

            },String.valueOf(i)).start();
        }

        for (int i = 1; i <= num; i++) {
            int finalI = i;
            new Thread(() -> {

                myCache.read(String.valueOf(finalI));

            },String.valueOf(i)).start();
        }
    }
}

/**
 *  Method is unlocked, resulting in queue jumping when writing
 */
class MyCache {
    private volatile Map<String, String> map = new HashMap<>();

    public void write(String key, String value) {
        System.out.println(Thread.currentThread().getName() + "The thread started writing");
        map.put(key, value);
        System.out.println(Thread.currentThread().getName() + "Thread write ok");
    }

    public void read(String key) {
        System.out.println(Thread.currentThread().getName() + "The thread started reading");
        map.get(key);
        System.out.println(Thread.currentThread().getName() + "Thread write read ok");
    }
}
2 The thread started writing
2 Thread write ok
3 The thread started writing
3 Thread write ok
1 The thread started writing    # A write from another thread was inserted, resulting in inconsistent data
4 The thread started writing
4 Thread write ok
1 Thread write ok
6 The thread started writing
6 Thread write ok
5 The thread started writing
5 Thread write ok
1 The thread started reading
1 Thread write read ok
2 The thread started reading
2 Thread write read ok
3 The thread started reading
3 Thread write read ok
4 The thread started reading
4 Thread write read ok
5 The thread started reading
6 The thread started reading
6 Thread write read ok
5 Thread write read ok

The synchronized weight lock and light lock can be used to ensure the reliability of data.

But this time we use a more fine-grained lock: ReadWriteLock read-write lock to ensure

public class ReadWriteLockDemo {
    public static void main(String[] args) {
        MyCache2 myCache = new MyCache2();
        int num = 6;
        for (int i = 1; i <= num; i++) {
            int finalI = i;
            new Thread(() -> {

                myCache.write(String.valueOf(finalI), String.valueOf(finalI));

            },String.valueOf(i)).start();
        }

        for (int i = 1; i <= num; i++) {
            int finalI = i;
            new Thread(() -> {

                myCache.read(String.valueOf(finalI));

            },String.valueOf(i)).start();
        }
    }

}
class MyCache2 {
    private volatile Map<String, String> map = new HashMap<>();
    private ReadWriteLock lock = new ReentrantReadWriteLock();

    public void write(String key, String value) {
        lock.writeLock().lock(); // Write lock
        try {
            System.out.println(Thread.currentThread().getName() + "The thread started writing");
            map.put(key, value);
            System.out.println(Thread.currentThread().getName() + "Thread write ok");

        }finally {
            lock.writeLock().unlock(); // Release write lock
        }
    }

    public void read(String key) {
        lock.readLock().lock(); // Read lock
        try {
            System.out.println(Thread.currentThread().getName() + "The thread started reading");
            map.get(key);
            System.out.println(Thread.currentThread().getName() + "Thread write read ok");
        }finally {
            lock.readLock().unlock(); // Release read lock
        }
    }
}
1 The thread started writing
1 Thread write ok
6 The thread started writing
6 Thread write ok
3 The thread started writing
3 Thread write ok
2 The thread started writing
2 Thread write ok
5 The thread started writing
5 Thread write ok
4 The thread started writing
4 Thread write ok
    
1 The thread started reading
5 The thread started reading
2 The thread started reading
1 Thread write read ok
3 The thread started reading
2 Thread write read ok
6 The thread started reading
6 Thread write read ok
5 Thread write read ok
4 The thread started reading
4 Thread write read ok
3 Thread write read ok
  • Table lock: when operating data, lock the whole table without deadlock. The probability of lock conflict is high and the concurrency is low.
  • For row locks, deadlocks occur. The probability of lock conflict is low and the concurrency is high.
  • Lock conflict: for example, after transaction A locks certain rows, transaction B locks them again. Locks cannot coexist, otherwise lock conflict will occur. (however, shared locks can coexist, shared locks and exclusive locks cannot coexist, nor can exclusive locks and exclusive locks.)
  • Shared lock is also known as read lock. When a transaction reads locks on certain rows, other transactions are allowed to read these rows, but are not allowed to write them, and other transactions are not allowed to lock these rows exclusively, but are allowed to read locks.
  • Exclusive lock is also called write lock. When a transaction has write locks on certain transactions, other transactions are not allowed to write, but are allowed to read. Other transactions are not allowed to lock these rows. Including write lock.

Read lock changes during reading, which will lead to deadlock


Lock degradation

//Demote read / write lock
public class Demo1 {

    public static void main(String[] args) {
        //Reentrant read-write lock object
        ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
        ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock();//Read lock
        ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock();//Write lock

        //Lock degradation   
        //1 get write lock
        writeLock.lock();
        System.out.println("atguigu");

		//2 acquire read lock
        readLock.lock();
        System.out.println("---read");
        
        //3 release the write lock
        //writeLock.unlock();

        //4 release the read lock
        //readLock.unlock();
    }
}

10. Blocking queue


When the queue is empty, the operation of getting elements from the queue will be blocked
When the queue is full, adding elements from the queue will be blocked
Threads trying to get elements from an empty queue will be blocked until other threads insert new elements into the empty queue
Threads trying to add new elements to a full queue will be blocked until other threads remove one or more elements from the queue or completely empty, making the queue idle and adding new elements later

1)BlockQueue
Is a subclass of Collection

When do we use blocking queues
Multithreaded concurrent processing, thread pool

BlockingQueue has four sets of APIs

modeThrow exceptionNo exception will be thrown and there is a return valueBlocking, waitingTimeout wait
add toaddofferputoffer(timenum.timeUnit)
Removeremovepolltakepoll(timenum,timeUnit)
Judge team leader elementelementpeek--
/**
     * Throw exception
     */
    public static void test1(){
        //The size of the queue that needs to be initialized
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);

        System.out.println(blockingQueue.add("a"));
        System.out.println(blockingQueue.add("b"));
        System.out.println(blockingQueue.add("c"));
        //Throw exception: java.lang.IllegalStateException: Queue full
//        System.out.println(blockingQueue.add("d"));
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        //If you remove one more
        //This will also cause java.util.NoSuchElementException to throw an exception
        System.out.println(blockingQueue.remove());
    }
=======================================================================================
/**
     * No exception is thrown and there is a return value
     */
    public static void test2(){
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println(blockingQueue.offer("a"));
        System.out.println(blockingQueue.offer("b"));
        System.out.println(blockingQueue.offer("c"));
        //Adding an element that cannot be added, using offer will only return false and will not throw an exception
        System.out.println(blockingQueue.offer("d"));

        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        //If there are no pop-up elements, it will only return null and will not throw an exception
        System.out.println(blockingQueue.poll());
    }
=======================================================================================
/**
     * Waiting has been blocked
     */
    public static void test3() throws InterruptedException {
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);

        //Always blocked and will not return
        blockingQueue.put("a");
        blockingQueue.put("b");
        blockingQueue.put("c");

        //If the queue is full, another element will be entered. In this case, the program will wait until when the queue has a position to enter again, and the program will not stop
//        blockingQueue.put("d");

        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());
        //If we do it again, this situation will also wait, and the program will keep running and blocking
        System.out.println(blockingQueue.take());
    }
=======================================================================================
/**
     * Wait timeout blocking
     *  In this case, it will also wait for the queue to have a location or a product, but the timeout will end
     */
    public static void test4() throws InterruptedException {
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        blockingQueue.offer("a");
        blockingQueue.offer("b");
        blockingQueue.offer("c");
        System.out.println("Start waiting");
        blockingQueue.offer("d",2, TimeUnit.SECONDS);  //The timeout is 2s. If it exceeds 2s, the wait will end
        System.out.println("End waiting");
        System.out.println("===========Value==================");
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println("Start waiting");
        blockingQueue.poll(2,TimeUnit.SECONDS); //We won't wait for more than two seconds
        System.out.println("End waiting");
    }

2) Synchronization queue
If the synchronization queue has no capacity, it can also be regarded as a queue with capacity of 1;
When you enter an element, you must wait for it to be taken out before you can put another element in it;
put method and take method;

Unlike other blockingqueues, Synchronized does not store elements;
If you put an element, you must take it out first, otherwise you can't put in the value!

And the take of SynchronousQueue uses lock lock to ensure thread safety.

public class SynchronousQueue {
    public static void main(String[] args) {
        BlockingQueue<String> synchronousQueue = new java.util.concurrent.SynchronousQueue<>();
        // Add element to net queue
        new Thread(() -> {
            try {
                System.out.println(Thread.currentThread().getName() + "put 01");
                synchronousQueue.put("1");
                System.out.println(Thread.currentThread().getName() + "put 02");
                synchronousQueue.put("2");
                System.out.println(Thread.currentThread().getName() + "put 03");
                synchronousQueue.put("3");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        // Extract element
        new Thread(()-> {
            try {
                System.out.println(Thread.currentThread().getName() + "take" + synchronousQueue.take());
                System.out.println(Thread.currentThread().getName() + "take" + synchronousQueue.take());
                System.out.println(Thread.currentThread().getName() + "take" + synchronousQueue.take());
            }catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }
}
==========
Output: Thread-0put 01
Thread-1take1
Thread-0put 02
Thread-1take2
Thread-0put 03
Thread-1take3

11. Thread pool

Thread pool: three methods, seven parameters and four rejection strategies

Pool technology
The operation of the program, essence: occupy the resources of the system! We need to optimize the use of resources = = > pooling technology
Thread pool, JDBC connection pool, memory pool, object pool, etc....
The creation and destruction of resources are very resource consuming

Pooling Technology: prepare some resources in advance. If someone wants to use them, come to me and return them to me after use, so as to improve efficiency.

1) Benefits of thread pool:
1. Reduce resource consumption;
2. Improve response speed;
3. Convenient management;
Thread reuse, maximum concurrent number can be controlled and threads can be managed;

2) Thread pool: three methods

  • ExecutorService threadPool = Executors.newSingleThreadExecutor();// One pool, one thread, single thread
  • ExecutorService threadPool2 = Executors.newFixedThreadPool(5); // Create a fixed thread pool size
  • ExecutorService threadPool3 = Executors.newCachedThreadPool(); // Scalable, expandable
//Demonstrate three common categories of thread pools
public class ThreadPoolDemo1 {
    public static void main(String[] args) {
        //One pool of five threads
        ExecutorService threadPool1 = Executors.newFixedThreadPool(5); //5 windows

        //One pool, one thread
        ExecutorService threadPool2 = Executors.newSingleThreadExecutor(); //A window

        //A pool of scalable threads
        ExecutorService threadPool3 = Executors.newCachedThreadPool();
        //10 customer requests
        try {
            for (int i = 1; i <=10; i++) {
                //threadPool1 execution, threadPool2
                threadPool3.execute(()->{
                    System.out.println(Thread.currentThread().getName()+" Handle the business");
                });
            }
        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            //close
            threadPool3.shutdown();
        }

    }

}

3) Seven parameters

public ThreadPoolExecutor(int corePoolSize,  //Core (resident) thread pool size
                          int maximumPoolSize, //Maximum thread pool size
                          long keepAliveTime,  //If no one calls it, it will be released
                          TimeUnit unit, //Timeout unit
                          BlockingQueue<Runnable> workQueue, //Blocking queue
                          ThreadFactory threadFactory, //Thread factories generally do not need to move when creating threads
                          RejectedExecutionHandler handler //Reject policy
                         ) {
    if (corePoolSize < 0 ||
        maximumPoolSize <= 0 ||
        maximumPoolSize < corePoolSize ||
        keepAliveTime < 0)
        throw new IllegalArgumentException();
    if (workQueue == null || threadFactory == null || handler == null)
        throw new NullPointerException();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}


For integer.max_ The initial value of value is large, so generally, we use the underlying ThreadPoolExecutor to create a thread pool.
Case:
The thread pool is created only when the execute() method is executed. The core thread is 2, the maximum thread is 5, and the length of the blocking queue is 3
When the third person comes, block the queue first. When the sixth person comes, create a new thread to process the sixth person. When the ninth person comes, execute the rejection policy

4) Reject policy

  1. new ThreadPoolExecutor.AbortPolicy() / /: the rejection policy is: when the bank is full, someone else comes in, does not handle this person's, and throws an exception to prevent the normal operation of the system. If the maximum load is exceeded, an exception will be thrown: queue capacity + maxPoolSize (default)

  2. new ThreadPoolExecutor.CallerRunsPolicy() / /: the rejection policy is: where you come from, where you go. The main thread handles it. It will neither abandon the task nor throw an exception, but return the task to the caller, so as to reduce the traffic of the new task

  3. new ThreadPoolExecutor.DiscardPolicy(): / / the rejection policy is: if the queue is full, it will not be processed and no exception will be thrown.

  4. New ThreadPoolExecutor. Discardolddestpolicy() / / the rejection policy is to discard the task that has been waiting the longest in the queue, then add the current task to the queue and try to submit the current task again

5) How to set the size of the thread pool
1. CPU intensive: the number of cores of a computer can be selected according to the number of cores; Select the size of the maximunPoolSize
CPU intensive is also called computing intensive, which means that the performance of the system's hard disk and memory is much better than that of the CPU. At this time, most of the system operation is CPU Loading 100%. The CPU needs to read / write I/O (hard disk / memory). I/O can be completed in a very short time, while the CPU still has many operations to process, and CPU Loading is very high.

// Gets the number of cores of the cpu
        int max = Runtime.getRuntime().availableProcessors();
        ExecutorService service =new ThreadPoolExecutor(
                2,
                max,
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()
        );

2. I/O intensive:
IO intensive means that the system interacts with I/O most of the time, and this time thread will not occupy the CPU for processing, that is, within this time range, other threads can use the CPU, so more threads can be configured.

12. Four functional interfaces

lambda expression, chain programming, functional interface, Stream flow calculation
Functional interface: an interface with only one method

1) Function function interface

public class FunctionDemo {
    public static void main(String[] args) {
        Function<String, String> function = (str) -> {return str;};
        System.out.println(function.apply("aaaaaaaaaa"));
        //aaaaaaaaaa
    }
}

2) Predicate type interface

public class PredicateDemo {
    public static void main(String[] args) {
        Predicate<String> predicate = (str) -> {return str.isEmpty();};
        System.out.println(predicate.test("aaa"));// false
        System.out.println(predicate.test(""));// true
    }
}

3) Supplier supply interface

/**
 * Supply type interface, return only, no input
 */
public class Demo4 {
    public static void main(String[] args) {
        Supplier<String> supplier = ()->{return "1024";};
        System.out.println(supplier.get());
    }
}

4) Consumer interface

/**
 * Consumer interface has no return value! Only input!
 */
public class Demo3 {
    public static void main(String[] args) {
        Consumer<String> consumer = (str)->{
            System.out.println(str);
        };
        consumer.accept("abc");
    }
}

13. Stream flow calculation

public class StreamDemo {
    public static void main(String[] args) {
        User u1 = new User(1, "a", 23);
        User u2 = new User(2, "b", 23);
        User u3 = new User(3, "c", 23);
        User u4 = new User(6, "d", 24);
        User u5 = new User(4, "e", 25);

        List<User> list = Arrays.asList(u1, u2, u3, u4, u5);
        // lambda, chain programming, functional interface, flow computing
        list.stream()
         		//1. Id must be an even number
                .filter(user -> {return user.getId()%2 == 0;})
                //2. Age must be greater than 23
                .filter(user -> {return user.getAge() > 23;})
                //3. Change user name to uppercase
                .map(user -> {return user.getName().toUpperCase();})
                //4. User name in reverse order
                .sorted((user1, user2) -> {return user2.compareTo(user1);})
                //5. Only one user can be output
                .limit(1)
                .forEach(System.out::println);
    }
}

14. ForkJoin branch merger

Execute tasks in parallel! Improve efficiency ~. In large amounts of data, the speed will be faster!

In big data: MapReduce core idea - > split big tasks into small tasks!

1) ForkJoin features: work theft!
The implementation principle is: double ended queue! You can get the task from above and below for execution!

2) How do I use ForkJoin?
1. Execute through ForkJoinPool
2. Compute task execute (forkjointask <? > task)
3. The calculation class should inherit ForkJoinTask;

Calculation class of ForkJoin

public class ForkJoinDemo extends RecursiveTask<Long> {
    private long star;
    private long end;
    /** critical value */
    private long temp = 1000000L;

    public ForkJoinDemo(long star, long end) {
        this.star = star;
        this.end = end;
    }

    @Override
    protected Long compute() {
        if ((end - star) < temp) {
            Long sum = 0L;
            for (Long i = star; i < end; i++) {
                sum += i;
            }
            return sum;
        }else {
            // Divide and conquer calculation using ForkJoin
            //1. Calculate the average value
            long middle = (star + end) / 2;
            ForkJoinDemo forkJoinDemo1 = new ForkJoinDemo(star, middle);
            // Split the task and push the thread into the thread queue
            forkJoinDemo1.fork();
            ForkJoinDemo forkJoinDemo2 = new ForkJoinDemo(middle, end);
            forkJoinDemo2.fork();
            long taskSum = forkJoinDemo1.join() + forkJoinDemo2.join();
            return taskSum;
        }
    }
}

Test class

public class ForkJoinTest {
    private static final long SUM = 20_0000_0000;

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        test1();
        test2();
        test3();
    }

    /**
     * Use common methods
     */
    public static void test1() {
        long star = System.currentTimeMillis();
        long sum = 0L;
        for (long i = 1; i < SUM ; i++) {
            sum += i;
        }
        long end = System.currentTimeMillis();
        System.out.println(sum);
        System.out.println("Time:" + (end - star));
        System.out.println("----------------------");
    }
    /**
     * Using the ForkJoin method
     */
    public static void test2() throws ExecutionException, InterruptedException {
        long star = System.currentTimeMillis();

        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ForkJoinTask<Long> task = new ForkJoinDemo(0L, SUM);
        ForkJoinTask<Long> submit = forkJoinPool.submit(task);
        Long along = submit.get();

        System.out.println(along);
        long end = System.currentTimeMillis();
        System.out.println("Time:" + (end - star));
        System.out.println("-----------");
    }
    /**
     * Using Stream flow calculation
     */
    public static void test3() {
        long star = System.currentTimeMillis();
        long sum = LongStream.range(0L, 20_0000_0000L).parallel().reduce(0, Long::sum);
        System.out.println(sum);
        long end = System.currentTimeMillis();
        System.out.println("Time:" + (end - star));
        System.out.println("-----------");
    }
}

. parallel().reduce(0, Long::sum) uses a parallel stream to calculate the whole calculation to improve efficiency.

public class TestState {
    public static void main(String[] args) {
        List<Integer> num = Arrays.asList(1, 2, 4, 5, 6, 7);
        System.out.println(System.currentTimeMillis());
        Integer result = num.stream().reduce((x, y) -> {
            System.out.println("x:"+x);
            return x * y;
        }).get();
        System.out.println("resutl:"+result);
        System.out.println(System.currentTimeMillis());
        Integer result2 = num.stream().parallel().reduce((x, y) -> {
            System.out.println("x:"+x);
            return x * y;
        }).get();
        System.out.println("resutl2:"+result2);
        System.out.println(System.currentTimeMillis());

        //You can also write it like this. The effect is the same. One is a Lambda expression and an anonymous inner class
        Integer integer = num.stream().reduce(new BinaryOperator<Integer>() {
            @Override
            public Integer apply(Integer a, Integer b) {

                return a + b;
            }
        }).get();
    }
}
====
1631887148268
x:1
x:2
x:8
x:40
x:240
resutl:1680
1631887148314 (54ms)
x:6
x:5
x:2
x:1
x:8
resutl2:1680
1631887148317 (3ms)

15. Asynchronous callback

The original intention of Future design: modeling the result of an event in the Future!

In fact, the front end -- > sends ajax asynchronous requests to the back end

But we usually use completable future
(1) runAsync asynchronous callback with no return value

public static void main(String[] args) throws ExecutionException, InterruptedException 
{
        // Initiate a request
        CompletableFuture<Void> future = CompletableFuture.runAsync(()->{
            //Initiate an asynchronous task
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }        System.out.println(Thread.currentThread().getName()+".....");
              //Execute after blocking   
        });
        System.out.println(System.currentTimeMillis());
        System.out.println("Execute first");
        //1. Output execution results
        System.out.println(future.get());  //2. Obtain execution results
 }

(2) Asynchronous callback supplyAsync with return value

//Asynchronous callback with return value
CompletableFuture<Integer> completableFuture=CompletableFuture.supplyAsync(()->{
    System.out.println(Thread.currentThread().getName());
    try {
        TimeUnit.SECONDS.sleep(2);
        int i=1/0;
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return 1024;
});
System.out.println(completableFuture.whenComplete((t, u) -> {
    //success callback
    System.out.println("t=>" + t); //Normal return result
    System.out.println("u=>" + u); //Error message for throwing exception
}).exceptionally((e) -> {
    //error callback
    System.out.println(e.getMessage());
    return 404;
}).get());

whenComplete: there are two parameters, one is t and the other is u
T: Is the result of the normal return of the representative;
U: Is the error message that represents the exception thrown;
If an exception occurs, get can get the value returned by exceptionally;

16. JMM

1) Understanding of Volatile
Volatile is a lightweight synchronization mechanism provided by Java virtual machine
1. Ensure visibility
2. Atomicity is not guaranteed
3. Prohibit instruction rearrangement

  • Visibility means that when multiple threads access the same variable, one thread modifies the value of the variable, and other threads can immediately see the modified value.
  • Instruction reordering. Generally speaking, the processor may optimize the input code in order to improve the operation efficiency of the program. It does not ensure that the execution order of each statement in the program is consistent with that in the code, but it will ensure that the final execution result of the program is consistent with that of the code.
    For example, who executes statement 1 and statement 2 first has no impact on the final program result, so it is possible that statement 2 executes first and statement 1 executes later.

How to achieve visibility
The shared variable modified by volatile variable returns one more line of assembly when writing:
0x01a3de1d:movb $0×0,0×1104800(%esi);0x01a3de24**:lock** addl $0×0,(%esp);
Instructions prefixed with Lock cause two things in multi-core processors.
1) Writes the data of the current processor cache line back to the system memory.
2) This write back operation will invalidate the data of the memory address cached in other CPUs.

Multiprocessor bus sniffing:
In order to improve the processing speed, the processor does not directly communicate with the memory, but reads the data in the system memory to the internal cache before operation, but the operation does not know when it will be written to the memory. If you write a variable declared volatile, the JVM will send a lock prefix instruction to the processor to write the data of the cache line where the variable is located back to the system memory. However, in multiprocessors, in order to ensure that the cache of each processor is consistent, the cache consistency protocol will be implemented. Each processor checks whether its cache value is expired by sniffing the data transmitted on the bus. If the processor finds that the memory address corresponding to its cache line has been modified, it will set the cache line of the current processor to an invalid state, When the processor modifies this data, it will read the database from the system memory to the processor cache again.
The most famous cache consistency protocol is Intel's MESI protocol, which ensures that the copies of shared variables used in each cache are consistent. Its core idea is: when the CPU writes data, if it finds that the operating variable is a shared variable, that is, there are copies of the variable in other CPUs, it will send a signal to inform other CPUs to set the cache line of the variable to invalid state. Therefore, when other CPUs need to read the variable, they find that the cache line of the variable in their own cache is invalid, Then it will be re read from memory.

2) What is JMM?
JMM: JAVA Memory Model, which does not exist, is a concept and an agreement!

Some synchronization conventions of JMM:
1. Before the thread is unlocked, the shared variable must be immediately flushed back to main memory;
2. Before locking, the thread must read the latest value in the main memory into the working memory;
3. Locking and unlocking are the same lock;

Threads are divided into working memory and main memory

8 operations:
Read: acts on the main memory variable, which transfers the value of a variable from the main memory to the working memory of the thread for subsequent load action;
load: the variable that acts on the working memory. It puts the read operation from the main memory into the working memory;
Use: it acts on the variables in the working memory. It transfers the variables in the working memory to the execution engine. Whenever the virtual machine encounters a value that needs to be used, it will use this instruction;
assign: a variable that acts on the working memory. It puts a value received from the execution engine into the variable copy of the working memory;
store: a variable that acts on the main memory. It transfers the value of a variable from the working memory to the main memory for subsequent write;
write: a variable that acts on the main memory. It puts the value of the variable obtained from the working memory by the store operation into the variable of the main memory;
lock: a variable that acts on the main memory and identifies a variable as a thread exclusive state;
unlock: a variable that acts on the main memory. It releases a locked variable, and the released variable can be locked by other threads;

Rules:

  1. One of read and load, store and write operations is not allowed to appear alone. That is, read must be loaded and store must be written
  2. The thread is not allowed to discard its latest assign operation, that is, after the data of the work variable has changed, it must inform the main memory
  3. A thread is not allowed to synchronize data without assign from working memory back to main memory
  4. A new variable must be born in main memory. Working memory is not allowed to directly use an uninitialized variable. This means that the variables must be assign ed and load ed before the use and store operations
  5. Only one thread can lock a variable at a time. After multiple locks, you must perform the same number of unlocks to unlock
  6. If you lock a variable, the value of this variable in all working memory will be cleared. Before the execution engine uses this variable, you must re load or assign to initialize the value of the variable
  7. If a variable is not locked, it cannot be unlocked. You cannot unlock a variable that is locked by another thread
    Before unlock ing a variable, you must synchronize the variable back to main memory

Problem: if thread B modifies the data, thread A does not know, and the program does not know that the value in main memory has been modified!

17. volatile

1) Ensure visibility

public class JMMDemo01 {
    // If volatile is not added, the program will loop
    // Adding volatile ensures visibility
    private volatile static Integer number = 0;

    public static void main(String[] args) {
        //main thread
        //Child thread 1
        new Thread(()->{
            while (number==0){
            }
        }).start();
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //Child thread 2
        new Thread(()->{
            while (number==0){
            }

        }).start();
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        number=1;
        System.out.println(number);
    }
}

2) Atomicity is not guaranteed
Atomicity: indivisible;

Thread A cannot be disturbed or divided when executing tasks. It either succeeds or fails at the same time.

public class VDemo02 {

    private static volatile int number = 0;

    public static void add(){
        number++; 
        //++Not an atomic operation, but two ~3 operations
        //
    }

    public static void main(String[] args) {
        //Theoretically, number = = = 20000

        for (int i = 1; i <= 20; i++) {
            new Thread(()->{
                for (int j = 1; j <= 1000 ; j++) {
                    add();
                }
            }).start();
        }

        while (Thread.activeCount()>2){
            //main  gc
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName()+",num="+number);
    }
}

How to ensure atomicity without lock and synchronized?

Using atomic classes

public class VDemo02 {

    private static volatile AtomicInteger number = new AtomicInteger();
    public static void add(){
//        number++;
        number.incrementAndGet();  //The bottom layer is the atomicity guaranteed by CAS
    }

    public static void main(String[] args) {
        for (int i = 1; i <= 20; i++) {
            new Thread(()->{
                for (int j = 1; j <= 1000 ; j++) {
                    add();
                }
            }).start();
        }

        while (Thread.activeCount()>2){
            //main  gc
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName()+",num="+number);
    }
}

3) Prohibit instruction rearrangement

What is instruction rearrangement?
The computer does not execute the programs we write as we write
Source code – > compiler optimization rearrangement – > instruction parallelism may also rearrange – > memory system may also rearrange – > execution
When the processor rearranges instructions, it will consider the dependence between data!

Possible impact result: premise: the four values of a, B, x, y are all 0 by default
Thread A x=a y=b
Thread B = 1 a = 2
Normal results: x = 0; y =0;

Thread A b=1 a=2
Thread B x=a y=b
It may appear in thread A. execute b=1 first, and then x=a;
It may occur in thread B. first execute a=2, and then execute y=b;
Then the possible results are as follows: x=2; y=1.

volatile avoids instruction rearrangement:
volatile will add a memory barrier, which can ensure the order of instructions in this barrier.

Memory barrier: CPU instruction. effect:
1. Ensure the execution sequence of specific operations;
2. Memory visibility of some variables can be guaranteed (with these features, the visibility of volatile implementation can be guaranteed)

4) Summary
volatile ensures visibility;
Atomicity cannot be guaranteed
Due to the memory barrier, instruction rearrangement can be avoided
Interviewer: do you know where to use this memory barrier most? Singleton mode

18. Singleton mode

1) Hungry Han style

public class Hungry {
    private byte[] data1=new byte[1024*1024];
    private byte[] data2=new byte[1024*1024];
    private byte[] data3=new byte[1024*1024];
    private byte[] data4=new byte[1024*1024];


    private Hungry(){
    }
    private final static Hungry hungry = new Hungry();

    public static Hungry getInstance(){
        return hungry;
    }

}

2) DCL lazy

//Lazy singleton mode
public class LazyMan {

    private static boolean key = false;

    private LazyMan(){
    //Class lock
        synchronized (LazyMan.class){
            if (key==false){
                key=true;
            }
            else{
                throw new RuntimeException("Do not attempt to use reflection to break exceptions");
            }
        }
        System.out.println(Thread.currentThread().getName()+" ok");
    }
    private volatile static LazyMan lazyMan;

    //Dual detection lock mode is referred to as DCL lazy mode for short
    public static LazyMan getInstance(){
        //Need to lock
        if(lazyMan==null){
            synchronized (LazyMan.class){
                if(lazyMan==null){
                    lazyMan=new LazyMan();
                    /** Not atomic operation
                     * 1,Allocate memory space
                     * 2,Execute the construction method to initialize the object
                     * 3,Point this object to this space
                     *
                     *  There is a possibility of instruction rearrangement
                     *  For example, the order of execution is 1, 3, 2, etc
                     *  We can add volatile to ensure instruction rearrangement
                     */
                }
            }
        }
        return lazyMan;
    }
    //Single thread is ok
    //But if it's concurrent
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
        //Reflection in Java
//        LazyMan instance = LazyMan.getInstance();
        Field key = LazyMan.class.getDeclaredField("key");
        key.setAccessible(true);
        Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true); //Ignoring private constructors
        LazyMan lazyMan1 = declaredConstructor.newInstance();
        key.set(lazyMan1,false);
        LazyMan instance = declaredConstructor.newInstance();

        System.out.println(instance);
        System.out.println(lazyMan1);
        System.out.println(instance == lazyMan1); 
    }
}

3) Static inner class

//Static inner class
public class Holder {
    private Holder(){

    }
    public static Holder getInstance(){
        return InnerClass.holder;
    }
    public static class InnerClass{
        private static final Holder holder = new Holder();
    }
}

Single case is not safe because of reflection

4) Enumeration

//What is enum? Enum itself is a Class class
public enum EnumSingle {
    INSTANCE;
    public EnumSingle getInstance(){
        return INSTANCE;
    }
}

class Test{
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        EnumSingle instance1 = EnumSingle.INSTANCE;
        Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
        declaredConstructor.setAccessible(true);
        //Java. Lang. nosuchmethodexception: com. OGJ. Single. Enumsingle. < init > () has no parameterless structure
        //Cannot reflectively create enum objects

        EnumSingle instance2 = declaredConstructor.newInstance();
        System.out.println(instance1 ==instance2);
    }
}

19. In depth understanding of CAS

CAS: compareandset compare and exchange

public class casDemo {
    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(2020);

        //boolean compareAndSet(int expect, int update)
        //Expected value, updated value
        //If the actual value is the same as my expectation, update it
        //If the actual value is different from my expectation, it will not be updated
        System.out.println(atomicInteger.compareAndSet(2020, 2021));
        System.out.println(atomicInteger.get());

        //Because the expected value is 2020, but the actual value becomes 2021, the modification will fail
        //CAS is the concurrency primitive of CPU
        atomicInteger.getAndIncrement(); //++Operation
        System.out.println(atomicInteger.compareAndSet(2020, 2021));
        System.out.println(atomicInteger.get());
    }
}



2) Summary
CAS: compare the value in the current working memory with the value in the main memory. If this value is expected, execute the operation! If you don't keep cycling, you use a spin lock.

Disadvantages:
The cycle will take time;
One time can only guarantee the atomicity of one shared variable;
It will have ABA problems

ABA problem (civet cat for Prince)

Thread 1: the expected value is 1 and becomes 2;
Thread 2: two operations:
1. The expected value is 1 and becomes 3
2. The expectation is 3 and becomes 1
Therefore, for thread 1, the value of A is still 1, so there is A problem and deceived thread 1;

public class casDemo {
    //CAS: compareandset compare and exchange
    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(2020);
		//=====Tamper lock===
        System.out.println(atomicInteger.compareAndSet(2020, 2021));
        System.out.println(atomicInteger.get());

        //boolean compareAndSet(int expect, int update)
 
        System.out.println(atomicInteger.compareAndSet(2021, 2020));
        System.out.println(atomicInteger.get());

        //========
        System.out.println(atomicInteger.compareAndSet(2020, 2021));
        System.out.println(atomicInteger.get());
    }
}

20. Atomic reference

To solve the ABA problem, the corresponding idea is to use the optimistic lock~

Atomic operation with version number!

Integer uses the object caching mechanism. The default range is - 128 ~ 127. It is recommended to use the static factory method valueOf to obtain object instances instead of new, because valueOf uses caching, and new will create new objects and allocate new memory space.

Atomic operation with version number

public class CASDemo {
    /**AtomicStampedReference Note that if the generic is a wrapper class, pay attention to the reference of the object
     * In normal business operations, objects are compared (override the equals method)
     * 
     */
    static AtomicStampedReference<Integer> atomicStampedReference = new
            AtomicStampedReference<>(3, 1);

    // CAS compareAndSet: compare and exchange!
    public static void main(String[] args) {
        new Thread(() -> {
            int stamp = atomicStampedReference.getStamp(); // Get version number
            System.out.println("a1=>" + stamp);
            
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // During modification, the version number is updated by + 1
            atomicStampedReference.compareAndSet(3, 5,
                    atomicStampedReference.getStamp(),
                    atomicStampedReference.getStamp() + 1);
            
            System.out.println("a2=>" + atomicStampedReference.getStamp());
            // Change the value back and update the version number + 1
            System.out.println(atomicStampedReference.compareAndSet(5, 3,atomicStampedReference.getStamp(),atomicStampedReference.getStamp() + 1));
            System.out.println("a3=>" + atomicStampedReference.getStamp());
        }, "a").start();
        
        // The principle of optimistic lock is the same!
        new Thread(() -> {
            int stamp = atomicStampedReference.getStamp(); // Get version number
            System.out.println("b1=>" + stamp);
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(atomicStampedReference.compareAndSet(3, 5,stamp, stamp + 1));
            System.out.println("b2=>" + atomicStampedReference.getStamp());
        }, "b").start();
    }
}
======
a1=>1
b1=>1
a2=>2
true
a3=>3
false
b2=>3

21. Understanding of various locks

1) Fair lock
Fair lock: very fair. You can't jump the queue. You must come first

Unfair lock: very unfair. Queue jumping is allowed and the order can be changed (unfair by default)

public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

2) Reentrant lock (recursive lock)

  1. Synchronized lock
public class Demo01 {
    public static void main(String[] args) {
        Phone phone = new Phone();
        new Thread(()->{
            phone.sms();
        },"A").start();
        new Thread(()->{
            phone.sms();
        },"B").start();
    }

}

class Phone{
    public synchronized void sms(){
        System.out.println(Thread.currentThread().getName()+"=> sms");
        call();//There is also a lock here
    }
    public synchronized void call(){
        System.out.println(Thread.currentThread().getName()+"=> call");
    }
}
  1. Lock lock
//lock
public class Demo02 {

    public static void main(String[] args) {
        Phone2 phone = new Phone2();
        new Thread(()->{
            phone.sms();
        },"A").start();
        new Thread(()->{
            phone.sms();
        },"B").start();
    }

}
class Phone2{

    Lock lock=new ReentrantLock();

    public void sms(){
        lock.lock(); //Details: This is two locks and two keys
        //The lock lock must be paired, otherwise it will deadlock in it
        try {
            System.out.println(Thread.currentThread().getName()+"=> sms");
            call();//There is also a lock here
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public void call(){
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + "=> call");
        }catch (Exception e){
            e.printStackTrace();
        }
        finally {
            lock.unlock();
        }
    }
}

lock locks must be paired, which means that the number of locks and unlock s must be the same;
The lock added outside can also be unlocked inside; The lock added inside can also be unlocked outside;

  1. Spin lock
  1. spinlock
public final int getAndAddInt(Object var1, long var2, int var4) {
    int var5;
    do {
        var5 = this.getIntVolatile(var1, var2);
    } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
    return var5;
}
  1. Self designed spin lock
public class SpinlockDemo {

    // default
    // int 0
    //thread null
    AtomicReference<Thread> atomicReference=new AtomicReference<>();

    //Lock
    public void myLock(){
        Thread thread = Thread.currentThread();
        System.out.println(thread.getName()+"===> mylock");

        //Spin lock
        while (!atomicReference.compareAndSet(null,thread)){
            System.out.println(Thread.currentThread().getName()+" ==> In spin~");
        }
    }


    //Unlock
    public void myUnlock(){
        Thread thread=Thread.currentThread();
        System.out.println(thread.getName()+"===> myUnlock");
        atomicReference.compareAndSet(thread,null);
    }

}

public class TestSpinLock {
    public static void main(String[] args) throws InterruptedException {
        ReentrantLock reentrantLock = new ReentrantLock();
        reentrantLock.lock();
        reentrantLock.unlock();


        //Spin lock using CAS
        SpinlockDemo spinlockDemo=new SpinlockDemo();
        new Thread(()->{
            spinlockDemo.myLock();
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                spinlockDemo.myUnlock();
            }
        },"t1").start();

        TimeUnit.SECONDS.sleep(1);

        new Thread(()->{
            spinlockDemo.myLock();
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                spinlockDemo.myUnlock();
            }
        },"t2").start();
    }
}
====
t1===> mylock   //t1 just got empty spin for the atomic class, and then sleep
t2===> mylock
t2 ==> In spin~
t1===> myUnlock
t2===> myUnlock

Operation results:
The t2 process must wait for the t1 process to Unlock before it can Unlock. Before that, spin and wait....

4) Deadlock

public class DeadLock {
    public static void main(String[] args) {
        String lockA= "lockA";
        String lockB= "lockB";

        new Thread(new MyThread(lockA,lockB),"t1").start();
        new Thread(new MyThread(lockB,lockA),"t2").start();
    }
}

class MyThread implements Runnable{

    private String lockA;
    private String lockB;

    public MyThread(String lockA, String lockB) {
        this.lockA = lockA;
        this.lockB = lockB;
    }

    @Override
    public void run() {
        synchronized (lockA){
            System.out.println(Thread.currentThread().getName()+" lock"+lockA+"===>get"+lockB);
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (lockB){
                System.out.println(Thread.currentThread().getName()+" lock"+lockB+"===>get"+lockA);
            }
        }
    }
}

1. Use jps to locate the process number. There is a jps in the bin directory of jdk
Command: jps -l

2. Use the jstack process number to find deadlock information

Posted by olygolfer on Mon, 20 Sep 2021 01:39:37 -0700