JUC study notes

Keywords: Java

1. What is JUC


java.util toolkit, package, classification

Business: common Thread code Thread
Runnable has no return value and its efficiency is relatively low compared with Callable!

2. Processes and threads

Process: a program, a collection of QQ.exe Music.exe programs;
A process can often contain multiple threads, at least one!
How many threads does Java have by default? 2 mian s, GC
Thread: open a process Typora, write, and save automatically (the thread is responsible)
For Java: Thread, Runnable, Callable
Can Java really start threads? can't get started

// Local methods, the underlying C + +, java can not directly operate the hardware
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 */
			}
		}
}
// Local methods, the underlying C + +, Java can not directly operate the hardware

Concurrent, parallel:

Concurrency (multiple threads operate on the same resource)
One CPU core can simulate multiple threads. The world's martial arts can only be broken quickly and alternate quickly

Parallel (multiple people walking together)
The CPU is multi-core, and multiple threads can be executed at the same time; Thread pool

package com.kuang.demo01; 
public class Test1 { 
	public static void main(String[] args) { // Obtain the number of CPU cores. / / CPU intensive, IO intensive 
		System.out.println(Runtime.getRuntime().availableProcessors()); 
	} 
}
  • How to view the number of CPU cores

    • Task Manager - Performance - CPU
    • My computer - right click management - Device Manager - processor
  • Threads have several states

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

wait/sleep difference:

1. From different classes
wait => Object
sleep => Thread

2. About lock release
wait will release the lock. sleep sleeps. sleep with the lock will not release!

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 does not need to catch exceptions
sleep must catch exceptions

3. Lock lock

Traditional Synchronized:

package com.kuang.demo01; 
// Basic ticket selling examples 
import java.time.OffsetDateTime;
/**
*True multithreaded development, development in the company, reduces coupling
*Thread is a separate resource class without any affiliated operations!
*1, Properties and methods
*/
public class SaleTicketDemo01 {
	public static void main(String[] args) {
	// Concurrency: multiple threads operate on the same resource class and throw the resource class into the thread
	Ticket ticket = new Ticket();

	// @Functional interface, jdk1.8 	 lambda expression (parameter) - > {code} 
	new Thread(()->{
		for (int i = 1; i < 40 ; i++) { 
			ticket.sale();
		}
	},"A").start();


	new Thread(()->{
		for (int i = 1; i < 40 ; i++) { 
		ticket.sale();
		}
	},"B").start();


	new Thread(()->{
		for (int i = 1; i < 40 ; i++) { 
			ticket.sale();
		}
	},"C").start();

	}
}

// Resource class OOP 
class Ticket {
	// Properties and methods
	private int number = 30;

	// How to sell tickets
	// synchronized essence: queue, lock
	public synchronized void sale(){ 
		if (number>0){
			System.out.println(Thread.currentThread().getName()+"Sold"+(number--)+"ticket,surplus:"+number);
		}
 	}

}

Lock interface:


Fair lock: first come, first served

Unfair lock: very unfair. You can jump the queue (default)

  • Synchronized
package com.xiaofan;

public class SaleTicketDemo01 {
    public static void main(String[] args) {
        // Concurrency: multiple threads operate on the same resource class and throw the resource class into the thread
        final Ticket ticket = new Ticket();

        // @Functional interface, jdk1.8 lambda expression (parameter) - > {code}
        new Thread(()->{ for (int i = 0; i < 31; i++) ticket.sale(); }, "A").start();
        new Thread(()->{ for (int i = 0; i < 31; i++) ticket.sale(); }, "B").start();
        new Thread(()->{ for (int i = 0; i < 31; i++) ticket.sale(); }, "C").start();
    }
}

// Resource class OOP
class Ticket {
    // Properties, methods
    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, Number of votes remaining:" + number);
        }
    }
}

  • Lock
package com.xiaofan;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class SaleTicketDemo02 {
    public static void main(String[] args) {
        // Concurrency: multiple threads operate on the same resource class and throw the resource class into the thread
        final Ticket2 ticket2 = new Ticket2();

        // @Functional interface, jdk1.8 lambda expression (parameter) - > {code}
        new Thread(()->{ for (int i = 0; i < 31; i++) ticket2.sale(); }, "A").start();
        new Thread(()->{ for (int i = 0; i < 31; i++) ticket2.sale(); }, "B").start();
        new Thread(()->{ for (int i = 0; i < 31; i++) ticket2.sale(); }, "C").start();

    }
}

// Lock Trilogy
// 1. new ReentrantLock();
// 2. lock.lock();
// 3. lock.unlock();  Unlock
class Ticket2 {
    // Properties, methods
    private int number = 30;

    private Lock lock = new ReentrantLock();

    // How to sell tickets
    public void sale() {

        lock.lock();

        try {
            if (number > 0) {
                System.out.println(Thread.currentThread().getName() + "Sold the third" + (number--) + "Ticket, Number of votes remaining:" + number);
            }
        }catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}
  • The difference between Synchronized and Lock
    1. Synchronized built-in Java keyword, Lock is a Java class
    2. Synchronized cannot determine the status of obtaining the lock. Lock can determine whether the lock has been obtained
    3. Synchronized will release the lock automatically. Lock must release the lock manually. If the lock is not released, it will deadlock!
    4. Synchronized thread 1 (lock acquisition, blocking), thread 2 (wait, silly, etc.); Lock lock doesn't have to wait
    5. Synchronized reentrant lock, non interruptible, non fair lock; Lock, reentrant lock, judge lock, unfair lock (can be set by yourself)
    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. Producers and consumers

4.1. Synchronized version of producer and consumer issues

Handwritten interview: Singleton mode, sorting algorithm, producer and consumer, deadlock

package com.xiaofan.pc;


/**
 * Communication problems between threads: producer and consumer problems! Wait for the ring to wake up
 * Threads alternately perform a and B operations, and the same variable num = 0
 * A num + 1
 * B num - 1
 */
public class A {
    public static void main(String[] args) {
        Data data = new Data();

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

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

// Judgment, waiting, business, notification
class Data {

    private int number = 0;

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

    //-1
    public synchronized void decrement() throws InterruptedException {
        if (number == 0) {
            this.wait();
        }
        number --;
        System.out.println(Thread.currentThread().getName() + " => " + number);
        // Notify other threads that I + 1 is over
        this.notifyAll();
    }
}
  • The problem exists. There are four threads a, B, C and D! spurious wakeup


Change if to while judgment!!!!

package com.xiaofan.pc;


/**
 * Communication problems between threads: producer and consumer problems! Wait for the ring to wake up
 * Threads alternately perform a and B operations, and the same variable num = 0
 * A num + 1
 * B num - 1
 */
public class A {
    public static void main(String[] args) {
        Data data = new Data();

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

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

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

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

// Judgment, waiting, business, notification
class Data {

    private int number = 0;

    //+1
    public synchronized void increment() throws InterruptedException {
        while (number != 0) {
            this.wait();
        }
        number ++;
        System.out.println(Thread.currentThread().getName() + " => " + number);
        // Notify other threads that I + 1 is over
        this.notifyAll();
    }

    //-1
    public synchronized void decrement() throws InterruptedException {
        while (number == 0) {
            this.wait();
        }
        number --;
        System.out.println(Thread.currentThread().getName() + " => " + number);
        // Notify other threads that I + 1 is over
        this.notifyAll();
    }
}

4.2. Producers and consumers of JUC version


Find Condition through Lock


Code implementation:

package com.xiaofan.pc;


import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Communication problems between threads: producer and consumer problems! Wait for the ring to wake up
 * Threads alternately perform a and B operations, and the same variable num = 0
 * A num + 1
 * B num - 1
 */
public class B {
    public static void main(String[] args) {
        Data1 data1 = new Data1();
        new Thread(()->{ for (int i = 0; i < 10; i++) data1.increment(); }, "A").start();
        new Thread(()->{ for (int i = 0; i < 10; i++) data1.decrement(); }, "B").start();
        new Thread(()->{ for (int i = 0; i < 10; i++) data1.increment(); }, "C").start();
        new Thread(()->{ for (int i = 0; i < 10; i++) data1.decrement(); }, "D").start();

    }
}

// Judgment, waiting, business, notification
class Data1 {

    private int number = 0;
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    //+1
    public void increment() {
        lock.lock();

        try{
            while (number != 0) {
                condition.await();
            }
            number ++;
            System.out.println(Thread.currentThread().getName() + " => " + number);
            // Notify other threads that I + 1 is over
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    //-1
    public void decrement() {
        lock.lock();

        try {
            while (number == 0) {
                condition.await();
            }
            number --;
            System.out.println(Thread.currentThread().getName() + " => " + number);
            // Notify other threads that I + 1 is over
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }


    }
}

4.3. Condition realizes accurate notification and wake-up


Code test:

package com.xiaofan.pc;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Multithreading turns printing A-B-C
 */
public class C {
    public static void main(String[] args) {
        Data3 data3 = new Data3();

        new Thread(()->{ for (int i = 0; i < 10; i++) data3.printA();}, "Thread 1").start();
        new Thread(()->{ for (int i = 0; i < 10; i++) data3.printB();}, "Thread 2").start();
        new Thread(()->{ for (int i = 0; i < 10; i++) data3.printC();}, "Thread 3").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 number = 1;  // 1A 2B 3C

    public void printA() {
        lock.lock();

        try {
            // Business - Judgment - Execution - Notification
            while(number != 1) {
                condition1.await();
            }
            number = 2;
            System.out.println(Thread.currentThread().getName() + "=> A...");
            // Wake up the designated person
            condition2.signal();

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }

    public void printB() {
        lock.lock();

        try {
            // Business - Judgment - Execution - Notification
            while(number != 2) {
                condition2.await();
            }
            number = 3;
            System.out.println(Thread.currentThread().getName() + "=> B...");
            condition3.signal();

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void printC() {
        lock.lock();

        try {
            // Business - Judgment - Execution - Notification
            while(number != 3) {
                condition3.await();
            }
            number = 1;
            System.out.println(Thread.currentThread().getName() + "=> C...");
            condition1.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }


}

5.8 lock phenomenon

How to judge who the lock is! Object Class

  • summary
    • new this is a concrete object
    • static Class is the only template
package com.kuang.lock8;

import java.util.concurrent.TimeUnit;

/**
*8 Locks are eight questions about locks
*1,Under standard conditions, do two threads print text messages or make calls first? 1 / send SMS 	 2 / call
*1,sendSms Delay 4 seconds, two threads print text messages or call first? 1 / send SMS 	 2 / call
*/
public class Test1 {
	public static void main(String[] args) { Phone phone = new Phone();

		//Existence of lock
		new Thread(()->{ 
			phone.sendSms();
		},"A").start();

		// capture
		try {
			TimeUnit.SECONDS.sleep(1);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		new Thread(()->{
			phone.call();
		},"B").start();
	}
}

class Phone{

	// The object of the synchronized lock is the caller of the method
	// The two methods use the same lock. Whoever gets it first will execute it!
	public synchronized void sendSms(){ 
		try {
			TimeUnit.SECONDS.sleep(4);
		} catch (InterruptedException e) { 
		e.printStackTrace();
		}
		System.out.println("send message");
	}

	public synchronized void call(){ 
		System.out.println("phone");
	}

}
package com.kuang.lock8;

import java.util.concurrent.TimeUnit;

	/**
	*3, After adding a common method! Texting first or Hello? Common method
	*4, Two objects, two synchronization methods, texting or calling// phone
	*/
	public class Test2	{
		public static void main(String[] args) {
			// Two objects, two callers, two locks! 
			Phone2 phone1 = new Phone2(); 
			Phone2 phone2 = new Phone2();

			//Existence of lock
			new Thread(()->{ 
				phone1.sendSms();
			},"A").start();

			// capture
			try {
				TimeUnit.SECONDS.sleep(1);
			} catch (InterruptedException e) { 
				e.printStackTrace();
			}

			new Thread(()->{ 
				phone2.call();
			},"B").start();
	}
}

class Phone2{

	// The object of the synchronized lock is the caller of the method!
	public synchronized void sendSms(){ 
		try {
			TimeUnit.SECONDS.sleep(4);
		} catch (InterruptedException e) { 
			e.printStackTrace();
		}
		System.out.println("send message");
	}

	public synchronized void call(){ 
		System.out.println("phone");
	}

	// There's no lock here! It is not a synchronization method and is not affected by locks
	public void hello(){ 
		System.out.println("hello");
	}

}
package com.kuang.lock8;

import java.util.concurrent.TimeUnit;

	/**
	*5,Add two static synchronization methods. There is only one object. Print and send SMS first? phone?
	*6,Two objects! Add two static synchronization methods, print and send SMS first? phone?
	*/
	public class Test3	{
		public static void main(String[] args) {
		// There is only one Class template for two objects, static, and the lock is Class 
		Phone3 phone1 = new Phone3();
		Phone3 phone2 = new Phone3();

		//Existence of lock
		new Thread(()->{ 
			phone1.sendSms();
		},"A").start();

		// capture
		try {
			TimeUnit.SECONDS.sleep(1);
		} catch (InterruptedException e) { 
			e.printStackTrace();
		}

		new Thread(()->{ 
			phone2.call();
		},"B").start();
	}
}

// Phone3 is the only Class object
class Phone3{

	// The object of the synchronized lock is the caller of the method!
	// Static static method
	// As soon as the Class is loaded! The lock is Class
	public static synchronized void sendSms(){ 
		try {
			TimeUnit.SECONDS.sleep(4);
		} catch (InterruptedException e) { 
			e.printStackTrace();
		}
		System.out.println("send message");
	}

	public static synchronized void call(){ 
		System.out.println("phone");
	}
}
package com.kuang.lock8;

import java.util.concurrent.TimeUnit;

	/**
	*1,1 A static synchronization method, a common synchronization method, an object, print and send text messages first? phone?
	*2,1 A static synchronization method, a common synchronization method, two objects, print and send text messages first? phone?
	*/
	public class Test4	{
		public static void main(String[] args) {
		// There is only one Class template for two objects, static, and the lock is Class 
		Phone4 phone1 = new Phone4();
		Phone4 phone2 = new Phone4();
		//Existence of lock
		new Thread(()->{ 
			phone1.sendSms();
		},"A").start();

		// capture
		try {
			TimeUnit.SECONDS.sleep(1);
		} catch (InterruptedException e) { 
		e.printStackTrace();
		}

		new Thread(()->{ 
			phone2.call();
		},"B").start();
	}
}

// Phone3 is the only Class object
class Phone4{

	// The static synchronization method locks the Class template
	public static synchronized void sendSms(){ 
		try {
			TimeUnit.SECONDS.sleep(4);
		} catch (InterruptedException e) { 
		e.printStackTrace();
		}
		System.out.println("send message");
	}

	// Common synchronization methods 	 Caller of lock
	public synchronized void call(){ 
	System.out.println("phone");
	}
}

6. The collection class is unsafe

6.1. Unsafe

package com.xiaofan.unsafe;

import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;

// java.util.ConcurrentModificationException concurrent modification exception!
public class ListTest {
    public static void main(String[] args) {
        // Single thread security
        /*List<String> list = Arrays.asList("1", "2", "3");
        list.forEach(System.out::println);*/

        // And send an ArrayList that's not safe? synchronized: 
        /**
         * Solution:
         * 1. List<String> list = new Vector<>();   Vector It comes from 1.0 and List comes from 1.2
         * 2. List<String> list = Collections.synchronizedList(new ArrayList<>());
         * 3. List<String> list = new CopyOnWriteArrayList<>();
         */
        // An optimization strategy of copying COW computer programming when CopyOnWrite is written
        // When multiple threads call, when the list is read, it is fixed and written (overwritten). Avoid overwriting when writing, resulting in data problems! Read write separation
        // What makes CopyOnWriteArrayList better than Vector is that Lock lock is applied at the bottom layer and synchronized is applied at the Vector
        List<String> list = new CopyOnWriteArrayList<>();

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

6.2. Unsafe

package com.xiaofan.unsafe;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;

// Similarly: java.util.ConcurrentModificationException concurrent modification exception!
public class SetTest {
    public static void main(String[] args) {

        /**
         * Solution:
         * 1. Set<String>set = Collections.synchronizedSet(new HashSet<>());
         * 2. Set<String>set = new CopyOnWriteArraySet<>();
         */
        Set<String>set = new CopyOnWriteArraySet<>();
        for (int i = 1; i <= 20; i++) {
            new Thread(()->{
                set.add(UUID.randomUUID().toString().substring(0, 5));
                System.out.println(set);
            }, String.valueOf(i)).start();
        }
    }
}
  • What is the bottom layer of HashSet?
public HashSet() {
    map = new HashMap<>();
}

// The essence of add set is that the key of map cannot be repeated!
public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}
// Constant value
private static final Object PRESENT = new Object();

6.3. Unsafe map

package com.xiaofan.unsafe;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;


// java.util.ConcurrentModificationException concurrent modification exception
public class MapTest {
    public static void main(String[] args) {

        /**
         * Solution:
         * 1. Map<String, String> map = Collections.synchronizedMap(new HashMap<>());
         * 2. Map<String, String> map = new ConcurrentHashMap<>();
         */
        Map<String, String> map = Collections.synchronizedMap(new HashMap<>());

        for (int i = 1; i <= 10; i++) {
            new Thread(()->{
                map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 5));
                System.out.println(map);
            }).start();
        }
    }
}

6.4. Research on HashMap data structure and integer power of 2










6.5. HashMap loading factor and transformation to red black tree














6.6. Principle of concurrenthashmap

7. Callable

  • Can have return value
  • Exceptions can be thrown
  • Method, run() / call()

Code test:


package com.xiaofan.callable;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class CallableTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
  		// Note: there is reuse in the same FutureTask
        FutureTask<String> futureTask1 = new FutureTask<>(new MyThread());
        FutureTask<String> futureTask2 = new FutureTask<>(new MyThread());
        new Thread(futureTask1, "A").start();
        new Thread(futureTask2, "B").start();    // The results are cached and efficient

        String result1 = futureTask1.get();       // This method may cause blocking, put it last, or use asynchronous communication to handle it
        System.out.println(result1);

        String result2 = futureTask1.get();
        System.out.println(result2);
    }
}


class MyThread implements Callable<String> {

    @Override
    public String call() {
        System.out.println(Thread.currentThread().getName() + " call...");
        return "1024";
    }
}

Details:

1. With cache
2. The result may need to wait and block!

8. Common auxiliary classes

For CountDownLatch, the focus is "one thread (multiple threads) waiting", while the other N threads can terminate or wait after completing "something". For CyclicBarrier, the focus is on multiple threads. If any thread is not completed, all threads must wait for each other and then continue to execute together.

CountDownLatch is a counter. Threads complete one record after another, but the count is not increasing but decreasing. The CyclicBarrier is more like a valve. It needs all threads to arrive before the valve can be opened and then continue to execute.

8.1.CountDownLatch

ID counter

package com.xiaofan;

import java.util.concurrent.CountDownLatch;

// Counter
public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        // The total number is 6. You can use it when you have to perform tasks!
        CountDownLatch countDownLatch = new CountDownLatch(6);

        for (int i = 1; i <= 6; i++) {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName() + " go out...");
                countDownLatch.countDown();     // Quantity minus 1
            }, String.valueOf(i)).start();
        }

        countDownLatch.await();     // Wait for the count to return to zero before proceeding down
        System.out.println("close door...");
    }
}

8.2. CyclicBarrier

Addition counter

package com.xiaofan;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierDemo {
    public static void main(String[] args) {
        /**
         * Gather 7 dragon balls to summon the Dragon
         */
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
            System.out.println("Summon dragon successfully!");
        });

        for (int i = 1; i <= 7; i++) {
            int temp = i;
            // Lambda operation failed i
            new Thread(()->{
                System.out.println(Thread.currentThread().getName() + "collect" + temp + " A dragon ball");

                try {
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                } catch (Exception e) {

                }

                System.out.println(Thread.currentThread().getName() + "continue...");
            }).start();
        }
    }
}

8.3. Semaphore

Semaphore

Grab six cars and grab three parking spaces at the same time

package com.xiaofan.util;

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class SemaphoreDemo {
    public static void main(String[] args) {
        // Number of threads, parking space, current limit!
        Semaphore semaphore = new Semaphore(3);

        for (int i = 1; i <= 6; i++) {
            new Thread(()->{
                // acquire() gets the semaphore
                try {
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName() + "Grab a parking space");
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println(Thread.currentThread().getName() + "Leave the parking space");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    semaphore.release();    // Release semaphore
                }
            }, String.valueOf(i)).start();
        }
    }
}

Principle:

semaphore.acquire(); Obtain the semaphore. If it is full, wait until it is released

semaphore.release(); Release the semaphore to wake up the waiting thread

Function: mutually exclusive use of multiple shared resources!

9. Read write lock

package com.xiaofan.rw;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * An exclusive lock (write lock) can only be held by one thread at a time
 * Shared lock (read lock) multiple threads can occupy at the same time
 * ReadWriteLock
 * Read read can coexist!
 * Read write cannot coexist!
 * Write write cannot coexist!
 */
public class ReadWriteLockDemo {
    public static void main(String[] args) {
//        MyCache myCache = new MyCache();
        MyCacheLock myCacheLock = new MyCacheLock();

        for (int i = 1; i <= 5; i++) {
            int temp = i;
            new Thread(()->{
                myCacheLock.put(String.valueOf(temp), String.valueOf(temp));
            }, String.valueOf(i)).start();
        }

        for (int i = 1; i <= 5; i++) {
            int temp = i;
            new Thread(()->{
                myCacheLock.get(String.valueOf(temp));
            }, String.valueOf(i)).start();
        }
    }
}

/**
 * Custom cache
 */
class MyCache {

    private volatile Map<String, Object> map = new HashMap<>();

    // Save, write
    public void put(String key, Object value) {
        System.out.println(Thread.currentThread().getName() +" write in " + key);
        map.put(key, value);
        System.out.println(Thread.currentThread().getName() +" Write complete!");
    }

    // Take, read
    public void get(String key) {
        System.out.println(Thread.currentThread().getName() + " read " + key);
        Object o = map.get(key);
        System.out.println(Thread.currentThread().getName() + " Read complete!");
    }
}

/**
 * Locked custom cache
 */
class MyCacheLock {

    private volatile Map<String, Object> map = new HashMap<>();
    // Read / write lock, more fine-grained control
    private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    // When saving and writing
    public void put(String key, Object value) {
        readWriteLock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() +" write in " + key);
            map.put(key, value);
            System.out.println(Thread.currentThread().getName() +" Write complete!");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.writeLock().unlock();
        }
    }

    // Take, read
    public void get(String key) {
        readWriteLock.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + " read " + key);
            Object o = map.get(key);
            System.out.println(Thread.currentThread().getName() + " Read complete!");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.readLock().unlock();
        }
    }
}

10. Blocking queue

When do we use blocking queues: multithreading, concurrent processing, thread pools!

Four sets of API s (one by one)

  1. Throw exception

  2. No exception will be thrown

  3. Blocking wait

  4. Timeout wait

package com.xiaofan.bq;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;

public class Test {
    public static void main(String[] args) throws InterruptedException {
        test4();
    }

    /**
     * Throw exception
     */
    public static void test1() {
        ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println(arrayBlockingQueue.add(1));
        System.out.println(arrayBlockingQueue.add(2));
        System.out.println(arrayBlockingQueue.add(3));

        // java.lang.IllegalStateException: Queue full
        // System.out.println(arrayBlockingQueue.add(4));
        // Judge team leader element
        System.out.println(arrayBlockingQueue.element());

        System.out.println(arrayBlockingQueue.remove());
        System.out.println(arrayBlockingQueue.remove());
        System.out.println(arrayBlockingQueue.remove());
        // java.util.NoSuchElementException
        // System.out.println(arrayBlockingQueue.remove());

    }

    /**
     * Do not throw exceptions
     */
    public static void test2() {
        ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println(arrayBlockingQueue.offer(1));
        System.out.println(arrayBlockingQueue.offer(2));
        System.out.println(arrayBlockingQueue.offer(3));

         System.out.println(arrayBlockingQueue.offer(4));

        // Judge team leader element
        System.out.println(arrayBlockingQueue.peek());

        System.out.println(arrayBlockingQueue.poll());
        System.out.println(arrayBlockingQueue.poll());
        System.out.println(arrayBlockingQueue.poll());
        System.out.println(arrayBlockingQueue.poll());
        // java.util.NoSuchElementException
        // System.out.println(arrayBlockingQueue.remove());

    }

    /**
     * Wait for blocking (always blocking)
     */
    public static void test3() throws InterruptedException {
        ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);
        arrayBlockingQueue.put(1);
        arrayBlockingQueue.put(2);
        arrayBlockingQueue.put(3);
        // Wait for blocking until it is put into position
        // arrayBlockingQueue.put(4);

        System.out.println(arrayBlockingQueue.take());
        System.out.println(arrayBlockingQueue.take());
        System.out.println(arrayBlockingQueue.take());
        // Wait for blocking until the element is retrieved
        System.out.println(arrayBlockingQueue.take());

    }

    /**
     * Wait blocking (timeout wait)
     */
    public static void test4() throws InterruptedException {
        ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println(arrayBlockingQueue.offer(1));
        System.out.println(arrayBlockingQueue.offer(2));
        System.out.println(arrayBlockingQueue.offer(3));
        // Results are generated after more than 3 seconds
        System.out.println(arrayBlockingQueue.offer(4, 3, TimeUnit.SECONDS));

        // Judge team leader element
        System.out.println(arrayBlockingQueue.peek());

        System.out.println(arrayBlockingQueue.poll());
        System.out.println(arrayBlockingQueue.poll());
        System.out.println(arrayBlockingQueue.poll());
        System.out.println(arrayBlockingQueue.poll(3, TimeUnit.SECONDS));


    }
}

10.1. SynchronousQueue synchronization queue

There is no capacity to put in an element. You must wait for it to be taken out before you can put another element in it!

put,take

import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;

/**
 * Synchronization queue
 * Unlike other blockingqueues, synchronous queues do not store elements
 * put If you enter an element, you must wait for take to come out before taking it again!
 */
public class SynchronousQueueDemo {
    public static void main(String[] args) {
        SynchronousQueue<String>synchronousQueue = new SynchronousQueue();

        new Thread(()->{
            try {
                System.out.println(Thread.currentThread().getName() + " put 1");
                synchronousQueue.put("1");
                System.out.println(Thread.currentThread().getName() + " put 2");
                synchronousQueue.put("2");
                System.out.println(Thread.currentThread().getName() + " put 3");
                synchronousQueue.put("3");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();

        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName() + " " + synchronousQueue.take());
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName() + " " + synchronousQueue.take());
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName() + " " + synchronousQueue.take());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }
}

11. Thread pool

Operation of the program, essence:
Occupy system resources! Optimize the use of resources! = > Pool technology
Thread pool, connection pool, memory pool and object pool / / create and destroy. Very wasteful of resources
Pooling Technology: prepare some resources in advance. If someone wants to use them, come to me and give them back to me after use.

Benefits of thread pool:
1. Reduce resource consumption
2. Improve response speed
3. Easy to manage.
Thread reuse, can control the maximum number of concurrent threads, and manage threads

  • Three methods of thread pool
package com.xiaofan.pool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

// Executors tool class, 3 methods
public class Demo01 {
    public static void main(String[] args) {
//        ExecutorService threadPool = Executors.newSingleThreadExecutor();
//         ExecutorService threadPool= Executors.newCachedThreadPool();
         ExecutorService threadPool = Executors.newFixedThreadPool(5);

        try {
            for (int i = 0; i < 10; i++) {
                // After using thread pool, use thread pool to create threads
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName() + " ok");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // When the thread pool runs out, the program ends. Close the thread pool
            threadPool.shutdown();
        }
    }
}
  • 7 major parameters
public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

// ThreadPool was called
public ThreadPoolExecutor(int corePoolSize,		// Core thread pool size
                          int maximumPoolSize,	// Maximum number of threads
                          long keepAliveTime,	// If no one calls it, it will be released
                          TimeUnit unit,		// Timeout unit
                          BlockingQueue<Runnable> workQueue,	// Blocking queue
                          ThreadFactory threadFactory,		// Thread factory, which creates threads, generally does not need to be moved
                          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.acc = System.getSecurityManager() == null ?
        null :
    AccessController.getContext();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}

  • Manually create a thread pool
package com.xiaofan.pool;

import java.util.concurrent.*;

/**
 * Custom thread pool
 * When no thread is available (the blocking queue is gone, the reject policy is started)
 * 1.new ThreadPoolExecutor.AbortPolicy()   When the thread pool is not enough and there are tasks, an exception is thrown
 * 2.new ThreadPoolExecutor.CallerRunsPolicy()  Where did it come from
 * 3.new ThreadPoolExecutor.DiscardPolicy()   If the queue is full, the task will be lost and no exception will be thrown
 * 4.new ThreadPoolExecutor.DiscardOldestPolicy()   When the queue is full, try to compete with the earliest, and no exception will be thrown
 */
public class Demo02 {
    public static void main(String[] args) {
        ExecutorService threadPool = new ThreadPoolExecutor(
                2,
                5,
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.DiscardOldestPolicy()
        );

        try {
            for (int i = 0; i < 10; i++) {
                // After using thread pool, use thread pool to create threads
//                threadPool.execute(()->{
//                    System.out.println(Thread.currentThread().getName() + " ok");
//                });

                threadPool.execute(new MyTask(i, String.valueOf(i)));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // When the thread pool runs out, the program ends. Close the thread pool
            threadPool.shutdown();
        }
    }
}

class MyTask implements Runnable {

    private int taskId;
    private String taskName;

    public MyTask(int taskId, String taskName){
        this.taskId = taskId;
        this.taskName = taskName;
    }

    public int getTaskId() {
        return taskId;
    }

    public void setTaskId(int taskId) {
        this.taskId = taskId;
    }

    public String getTaskName() {
        return taskName;
    }

    public void setTaskName(String taskName) {
        this.taskName = taskName;
    }

    @Override
    public void run() {
        System.out.println("ID: " + this.taskId + " NAME: "+Thread.currentThread().getName() + " ok");
    }

    public String toString(){
        return Integer.toString(this.taskId);
    }

}
  • 4 rejection strategies

/**
*new ThreadPoolExecutor.AbortPolicy() // When the bank is full, someone else comes in. If you don't deal with this person, throw an exception
*new ThreadPoolExecutor.CallerRunsPolicy() // Where to go!
*new ThreadPoolExecutor.DiscardPolicy() //If the queue is full, lose the task and no exception will be thrown!
*new ThreadPoolExecutor.DiscardOldestPolicy() //If the queue is full, try to compete with the earliest, and no exceptions will be thrown!
*/
  • How to set the size of thread pool!
package com.xiaofan.pool;

import java.util.concurrent.*;

/**
 * The maximum number of thread pools is set
 * 1. CPU Intensive, several cores, that is, several cores, can maintain the highest CPU efficiency!
 * 2. IO Intensive, which is greater than the number of IO consuming threads in the judgment program, half and twice
 */
public class Demo03 {
    public static void main(String[] args) {
        ExecutorService threadPool = new ThreadPoolExecutor(2,
                Runtime.getRuntime().availableProcessors(),
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.DiscardOldestPolicy()
        );

        try {
            for (int i = 0; i < 10; i++) {
                // After using thread pool, use thread pool to create threads
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName() + " ok");
                });

            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // When the thread pool runs out, the program ends. Close the thread pool
            threadPool.shutdown();
        }
    }
}

12. Four functional interfaces

Programmers in the new era: lambda expression, concatenated programming, functional interface, Stream streaming computing

Functional interface: an interface with only one method

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}
// Generics, enumerations, reflection
// lambda expression, chain programming, functional interface, Stream flow calculation
// Super multi functional interface
// Simplify the programming model and apply it at the bottom of the new version of the framework!
// Foreach (functional interface of consumer class)

  • Functional interface Function:
package com.xiaofan.function;

import java.util.function.Function;

/**
 * Function Functional interface with one input parameter and one output parameter
 * Functional interfaces can be simplified with lambda expressions
 */
public class Demo01 {
    public static void main(String[] args) {
        Function<String, String> function = new Function<String, String>() {

            @Override
            public String apply(String str) {
                return str;
            }
        };

        System.out.println(function.apply("fanfan"));

        // lambda expressions 
        function = str -> str;
        System.out.println(function.apply("xiaofan"));


    }
}
  • Predicate interface:
package com.xiaofan.function;

import java.util.function.Predicate;

/**
 * Deterministic interface: an input parameter. The return value can only be boolean
 */
public class Demo02 {
    public static void main(String[] args) {
        Predicate<String> predicate = new Predicate<String>() {

            @Override
            public boolean test(String s) {
                return s.isEmpty();
            }
        };

        System.out.println(predicate.test(""));

        // lambda expressions 
        predicate = str -> str.isEmpty();
        System.out.println(predicate.test("fanfan"));


    }
}
  • Supply interface Supplier
package com.xiaofan.function;

import java.util.function.Supplier;

/**
 * Supply type interface: no parameters, only return values
 */
public class Demo03 {
    public static void main(String[] args) {
        Supplier<String> supplier = new Supplier<String>() {

            @Override
            public String get() {
                return "1024";
            }
        };
        System.out.println(supplier.get());

        //lambda
        supplier = ()-> "1024";
        System.out.println(supplier.get());
    }
}
  • Consumer interface consumer
package com.xiaofan.function;

import java.util.function.Consumer;

/**
 * Consumer interface, no return value, only parameters
 */
public class Demo04 {
    public static void main(String[] args) {
        Consumer<String> consumer = new Consumer<String>() {

            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        };

        consumer.accept("xiaofan");
        //lambda expressions 
        consumer = s -> System.out.println(s);
        consumer.accept("xiaofan");
    }
}

13. Stream flow calculation

package com.xiaofan.stream;

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

/**
 * Title Requirements: complete this title in one minute: it can only be realized with one line of code!
 * There are 5 users! Filter:
 * 1. ID Must be even
 * 2. Age must be greater than 23
 * 3. User name to uppercase
 * 4. User names are sorted alphabetically backwards
 * 5. Output only one user!
 */
public class Test {
    public static void main(String[] args) {
        User u1 = new User(1, "a", 21);
        User u2 = new User(2, "b", 22);
        User u3 = new User(3, "c", 23);
        User u4 = new User(4, "d", 24);
        User u5 = new User(6, "e", 25);
        // A collection is storage
        List<User> users = Arrays.asList(u1, u2, u3, u4, u5);
        users.stream()
                .filter(u -> u.getId() % 2 == 0)
                .filter(u -> u.getAge() > 23)
                .map(u -> u.getName().toUpperCase())
                .sorted(Comparator.reverseOrder())
                .limit(1)
                .forEach(System.out::println);
    }
}

14. Branch merge ForkJoin

ForkJoin executes tasks in parallel in JDK 1.7! increase of efficiency. Large amount of data!
Big data: Map Reduce (splitting big tasks into small tasks)


ForkJoin:

package com.xiaofan.forkjoin;

import java.util.concurrent.RecursiveTask;

/**
 * The task of summation calculation!
 * 3000 6000(ForkJoin) 9000(Stream Parallel stream)
 * How to use forkjoin
 * 1. ForkjoinPool Execute through it
 * 2. Calculate the task forkjoinPool.execute(ForkJoinTask task)
 * 3. The calculation class should inherit ForkJoinTask
 */
public class ForkJoinDemo extends RecursiveTask<Long> {

    private Long start;
    private Long end;
    private Long temp = 10000L;

    public ForkJoinDemo(Long start, Long end) {
        this.start = start;
        this.end = end;
    }


    @Override
    protected Long compute() {
        if ((end - start) < temp) {
            Long sum = 0L;
            for (Long i = start; i <= end; i++) {
                sum += i;
            }
            return sum;
        } else {
            // forkjoin recursion
            Long middle = (start + end) /2;
            // Split the task and put it into the thread queue
            ForkJoinDemo task1 = new ForkJoinDemo(start, middle);
            task1.fork();
            // Split the task and put it into the thread queue
            ForkJoinDemo task2 = new ForkJoinDemo(middle+1, end);
            task2.fork();

            return task1.join() + task2.join();
        }
    }
}


  • test
package com.xiaofan.forkjoin;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.LongStream;

public class Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        test1();    // 7119
        test2();      // 5038
        test3();    // 180
    }

    public static void test1() {
        Long sum = 0L;
        long start = System.currentTimeMillis();
        for (Long i = 0L; i <= 10_1000_0000; i++) {
            sum += i;
        }
        long end = System.currentTimeMillis();
        System.out.println("sum= " + sum + " Time:" + (end -start));
    }

    public static void test2() throws ExecutionException, InterruptedException {
        long start = System.currentTimeMillis();
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ForkJoinDemo forkJoinDemo = new ForkJoinDemo(0L, 10_0000_0000L);
        ForkJoinTask<Long> submit = forkJoinPool.submit(forkJoinDemo);// Submit task
        Long sum = submit.get();

        long end = System.currentTimeMillis();
        System.out.println("sum= " + sum + " Time:" + (end - start));
    }

    public static void test3() throws ExecutionException, InterruptedException {
        long start = System.currentTimeMillis();
        long sum = LongStream.rangeClosed(0L, 10_0000_0000L).parallel().reduce(0, Long::sum);
        long end = System.currentTimeMillis();
        System.out.println("sum= " + sum + " Time:" + (end -start));
    }

}

15. Asynchronous callback

The original intention of Future design: to model the result of an event in the Future

  • runAsync with no return value
package com.xiaofan.feature;


import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

/**
 * Call completable future asynchronously
 * Asynchronous execution
 * Successful callback
 * Failed callback
 */
public class Demo01 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // runAsync asynchronous callback with no return value
        Future<Void> future = CompletableFuture.runAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread().getName() + " runAsync => Void");
        });

        System.out.println("111");
        future.get();   // Get blocking execution results
    }
}
  • supplyAsync with return value
package com.xiaofan.feature;


import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

/**
 * Call completable future asynchronously
 * Asynchronous execution
 * Successful callback
 * Failed callback
 */
public class Demo02 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // SupplyAsync asynchronous callback with return value
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName() + " supplyAsync => String");
//            int i = 1/0;
            return "1024";
        });

        System.out.println(future.whenComplete((t, u) -> {
            System.out.println("t=> " + t);     // Tnormal return results
            System.out.println("u=> " + u);     // u error message
        }).exceptionally((e) -> {
            System.out.println(e.getMessage());
            return "500";
        }).get());

    }
}

16. JMM

JMM: java Memory Model, nonexistent things, concepts, conventions!

  • Some synchronization conventions about JMM:

    1. The shared variable must be flushed back to main memory immediately before the thread is unlocked
    2. Before locking a thread, you must read the latest value in main memory into working memory!
    3. Locking and unlocking must be the same lock
  • Thread working memory, main memory

    • 8 operations

There are 8 kinds of memory interactive operations. The virtual machine implementation must ensure that each operation is atomic and cannot be separated (for variables of double and long types, exceptions are allowed for load, store, read and write operations on some platforms)

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

write: acts on a variable in main memory. It puts the value of the variable obtained from the working memory by the store operation into the variable in main memory

JMM formulates the following rules for the use of these eight instructions:
One of read and load, store and write operations is not allowed to appear alone. That is, read load must be used and store must be used
write
The thread is not allowed to discard its recent assign operation, that is, after the data of the work variable is changed, the main memory must be informed that a thread is not allowed to synchronize the data without assign from the working memory back to the main memory
A new variable must be born in main memory. Working memory is not allowed to directly use an uninitialized variable. This means that the assign and load operations must be performed before the use and store operations are performed on the linked variables
Only one thread can lock a variable at a time. After multiple locks, you must perform the same number of unlocks to unlock
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
If a variable is not locked, it cannot be unlocked. You cannot unlock a variable locked by other threads. Before unlocking a variable, you must synchronize the variable back to main memory

Problem: the program does not know that the value of main memory has been modified

17. volatile

  • Talk about your 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
  • Ensure visibility

package com.xiaofan.tvolitale;

import java.util.concurrent.TimeUnit;

public class JMMDemo {

    // Without volatile, the program will loop
    // Adding volatile ensures visibility
    private volatile static int num = 0;

    public static void main(String[] args) {
        new Thread(()->{ // Thread 1 does not know the change of main memory
            while (num == 0) {

            }
        }).start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        num = 1;
        System.out.println(num);
    }
}
  • Atomicity is not guaranteed javap -c *.class

Atomicity: indivisible

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

package com.xiaofan.tvolitale;

public class VDemo {
    // volatile does not guarantee atomicity
    private static int num = 0;

    public static void add() {
        num ++;
    }

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

        while(Thread.activeCount() > 2) {   // main gc
            Thread.yield();
        }

        System.out.println(Thread.currentThread().getName() + " " + num);
    }
}

How to ensure atomicity without Lock and synchronized

  • Using atomic classes

package com.xiaofan.tvolitale;

import java.util.concurrent.atomic.AtomicInteger;

public class VDemo {
    // volatile does not guarantee atomicity
    // Integer of atomic class
    private static AtomicInteger num = new AtomicInteger();

    public static void add() {
        // num ++;  Not an atomic operation
        num.getAndIncrement();  // +1 , CAS
    }

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

        while(Thread.activeCount() > 2) {   // main gc
            Thread.yield();
        }

        System.out.println(Thread.currentThread().getName() + " " + num);
    }
}

The bottom of these classes are directly linked to the operating system! Modify the value in memory! Unsafe class is a very special existence!

  • Instruction rearrangement

What is instruction rearrangement: the computer does not execute the program you write as you write.
Source code – > compiler optimized rearrangement – > instruction parallelism may also rearrange – > memory system may also rearrange - > execution
When the processor rearranges instructions, consider the dependency between data!

int x = 1; // 1
int y = 2; // 2 x = x + 5; // 3 y = x * x; // 4

What we expect: 1234	But it may become 2134 when executed	1324
 Can it be	4123!

Possible impact results: the four values of a, B, x, y are all 0 by default

volatile instructions can avoid instruction choreography

Memory barrier CPU instruction, function:

  1. Ensure the execution sequence of specific operations
  2. Memory visibility of some variables can be guaranteed

Volatile s can maintain visibility. Atomicity cannot be guaranteed. Due to the memory barrier, instruction rearrangement can be avoided!

18. Deep single instance mode

  • Hungry Han style
package com.xiaofan.single;

/**
 * Hungry Han style single case
 */
public class HungryMan {
    // It may waste space
    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 HungryMan() {}

    private final static HungryMan HUNGRY = new HungryMan();

    public static HungryMan getInstance() {
        return HUNGRY;
    }
}
  • Lazy style

Double detection can not avoid reflection damage

package com.xiaofan.single;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

import java.lang.reflect.Constructor; 
import java.lang.reflect.Field;
/**
 * Lazy style
 */
public class LazyMan {

    private static boolean qinjiang = false;
    
    private LazyMan() {
        System.out.println(Thread.currentThread().getName() + "ok");
        synchronized (LazyMan.class) {
            if (lazyMan != null) {
                throw new RuntimeException("Do not destroy the singleton through reflection!");
            }
        }

    }
    // Prohibit command replay
    private volatile static LazyMan lazyMan;

    // Dual detection lock mode lazy type single case DCL lazy type
    public static LazyMan getInstance() {
        if (lazyMan == null) {
            synchronized (LazyMan.class) {
                if (lazyMan == null) {
                    lazyMan = new LazyMan();    // Not an atomic operation
                    /**
                     * 1. Allocate memory space
                     * 2. Execute the construction method to initialize the object
                     * 3. Point this object to this space
                     *
                     * 123
                     * 132  A
                     *      B // At this time, lazyMan has not completed the construction
                     */
                }
            }
        }
        return lazyMan;
    }

    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
//        LazyMan lazyMan1 = LazyMan.getInstance();
        // Single case of destruction by reflection
        Constructor<LazyMan> constructor = LazyMan.class.getDeclaredConstructor();
        constructor.setAccessible(true);
        LazyMan lazyMan1 = constructor.newInstance();
        LazyMan lazyMan2 = constructor.newInstance();

        System.out.println(lazyMan1);
        System.out.println(lazyMan2);
    }
}
/**
*1. Allocate memory space
*2,Execute the construction method to initialize the object
*3,Point this object to this space
*
* 123
* 132 A
*B // At this time, lazyMan has not completed the construction
*/

Static inner class:

package com.kuang.single;

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


public static Holder getInstace(){ return InnerClass.HOLDER;
}


public static class InnerClass{
private static final Holder HOLDER = new Holder();
}

Single case unsafe, reflex

  • Enumeration implements singleton (enumeration itself is a kind)
    • Eliminate reflection damage and throw exceptions
    • After serialization, it is still the same object
package com.xiaofan.single;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * Lazy style
 */
public class LazyManApp {
    // Privatization construction method
    private LazyManApp() {}


    //Define a static enumeration class
    static enum SingletonEnum{
        //Create an enumeration object that is inherently singleton
        INSTANCE;

        private LazyManApp lazyManApp;

        //Constructor for privatizing enumerations
        private SingletonEnum(){
            lazyManApp = new LazyManApp();
        }
        public LazyManApp getInstance(){
            return lazyManApp;
        }
    }

    //Expose a static method to get LazyManApp object
    public static LazyManApp getInstance(){
        return LazyManApp.SingletonEnum.INSTANCE.getInstance();
    }


    public static void main(String[] args) throws IOException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
        test1();
    }

    // Test serialization
    public static void test3() throws IOException, ClassNotFoundException {
        SingletonEnum s = SingletonEnum.INSTANCE;
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("SerEnumSingleton.obj"));
        oos.writeObject(s);
        oos.flush();
        oos.close();

        FileInputStream fis = new FileInputStream("SerEnumSingleton.obj");
        ObjectInputStream ois = new ObjectInputStream(fis);
        SingletonEnum s1 = (SingletonEnum)ois.readObject();
        ois.close();
        System.out.println(s+"\n"+s1);
        System.out.println("Enumerate whether the two before and after serialization are the same:"+(s==s1));
    }

    // Test reflection
    public static void test2() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Constructor<SingletonEnum> constructor = SingletonEnum.class.getDeclaredConstructor(String.class, int.class);
        constructor.setAccessible(true);
        SingletonEnum lazyMan1 = constructor.newInstance();
        SingletonEnum lazyMan2 = constructor.newInstance();
        System.out.println(lazyMan1.getInstance());
        System.out.println(lazyMan2.getInstance());
    }

    // Test concurrency
    public static void test1() {
        for (int i = 0; i < 20; i++) {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName() + LazyManApp.getInstance());
            }).start();
        }
    }
}


Final decompiled source code of enumeration type:

/ Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3)
// Source File Name:	EnumSingle.java


package com.kuang.single;



public final class EnumSingle extends Enum
{


public static EnumSingle[] values()
{
return (EnumSingle[])$VALUES.clone();
}

public static EnumSingle valueOf(String name)
{
return (EnumSingle)Enum.valueOf(com/kuang/single/EnumSingle, name);
}

private EnumSingle(String s, int i)
{
super(s, i);
}

public EnumSingle getInstance()
{
return INSTANCE;
}

public static final EnumSingle INSTANCE; private static final EnumSingle $VALUES[];

static
{
INSTANCE = new EnumSingle("INSTANCE", 0);
$VALUES = (new EnumSingle[] { INSTANCE
});
}
}

19. In depth understanding of CAS

package com.xiaofan.cas;

import java.util.concurrent.atomic.AtomicInteger;

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

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

        System.out.println(atomicInteger.compareAndSet(2020, 2021));
        System.out.println(atomicInteger.get());
    }
}
  • Unsafe:



    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 not, keep cycling!
    Disadvantages:
    1. The cycle takes time
    2. One time can only guarantee the atomicity of one shared variable
    3. ABA problem

  • ABA problem

package com.kuang.cas;

import java.util.concurrent.atomic.AtomicInteger;

public class CASDemo {

// CAS 	 compareAndSet: compare and exchange!
	public static void main(String[] args) {
		AtomicInteger atomicInteger = new AtomicInteger(2020);

		// Expectations, updates
		// public final boolean compareAndSet(int expect, int update)
		// If my expected value is reached, I will update it. Otherwise, I will not update it. CAS is the concurrency primitive of CPU!
		// ==============Troublemaker thread==================
		System.out.println(atomicInteger.compareAndSet(2020, 2021));
		System.out.println(atomicInteger.get());

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

		// ==============Expected thread==================
		System.out.println(atomicInteger.compareAndSet(2020, 6666));
		System.out.println(atomicInteger.get());
	}
}

20. Atomic reference

Solve the ABA problem, introduce atomic reference, and the corresponding idea: optimistic lock!

Atomic operation with version number!

package com.xiaofan.cas;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicStampedReference;

public class CasSolveABADemo {

    static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference(1, 1);

    public static void main(String[] args) {
        // AtomicStampedReference note: if the generic is a wrapper class, pay attention to the reference of the object
        // In normal business operations, objects are compared one by one

        new Thread(()->{
            int stamp = atomicStampedReference.getStamp(); // Get version number
            System.out.println("a1=> " + stamp);

            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println("a2 => " + atomicStampedReference.compareAndSet(1, 2, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1));
            System.out.println("a2 => " + atomicStampedReference.getStamp());

            System.out.println("a3 => " + atomicStampedReference.compareAndSet(2, 1, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1));
            System.out.println("a3 => " + atomicStampedReference.getStamp());

        }, "a").start();


        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("b2 => " + atomicStampedReference.compareAndSet(1, 6, stamp, stamp + 1));
            System.out.println("b2 => " + atomicStampedReference.getStamp());
        }, "b").start();

    }
}

be careful:
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;

21. Reentrant lock, fair lock, unfair lock, spin lock, deadlock

  • Reentrant lock (recursive lock)

    • Lock sleeve lock for lock, locking and unlocking must be paired
  • Fair and unfair lock

  • Spin lock


Custom Lock test:

package com.kuang.lock;

import java.util.concurrent.atomic.AtomicReference;

/**
* Spin lock
*/
public class SpinlockDemo {

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

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

	// Spin lock
	while (!atomicReference.compareAndSet(null,thread)){

	}
}


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

}

Test:

package com.kuang.lock;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;


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

	// Spin lock CAS used in the bottom layer
	SpinlockDemo lock = new SpinlockDemo();



	new Thread(()-> { 
		lock.myLock();
		try {
			TimeUnit.SECONDS.sleep(5);
		} catch (Exception e) { e.printStackTrace();
		} finally {
			lock.myUnLock();
		}


	},"T1").start();


	TimeUnit.SECONDS.sleep(1);


	new Thread(()-> { 
		lock.myLock();

		try {
			TimeUnit.SECONDS.sleep(1);
		} catch (Exception e) { e.printStackTrace();
		} finally {
			lock.myUnLock();
		}


	},"T2").start();

	}
}

  • deadlock

  • View process number: jps -l
  • View stack information: jstack process number

Posted by parkej60 on Wed, 06 Oct 2021 06:35:28 -0700