JUC concurrent programming

Posted by loquaci on Sat, 22 Jan 2022 02:45:46 +0100

Thread six states

  • New: a new thread object has been created, but the start() method has not been called yet.
  • Runnable: the two states of ready and running in Java threads are generally called "running".
    After the thread object is created, other threads (such as main thread) call the start() method of the object. The thread in this state is located in the runnable thread pool, waiting to be selected by thread scheduling to obtain the use right of CPU. At this time, it is in ready state. The thread in the ready state becomes running after obtaining the CPU time slice.
  • Blocked: indicates that the thread is blocked on a lock.
  • Waiting: the thread entering this state needs to wait for other threads to make some specific actions (notification or interrupt).
  • Timed_WAITING: this state is different from WAITING. It can return after a specified time.
  • Terminated: indicates that the thread has completed execution

synchronized and Lock

synchronized

public class Client {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        //Multiple threads operate on the same resource class and throw the resource class into the thread
        new Thread(()->{
            for (int i = 0; i < 20; i++) {
                ticket.sale();
            }
        }, "Zhang San").start();
        new Thread(()->{
            for (int i = 0; i < 20; i++) {
                ticket.sale();
            }
        }, "Li Si").start();
        new Thread(()->{
            for (int i = 0; i < 20; i++) {
                ticket.sale();
            }
        }, "Wang Wu").start();
    }
}

class Ticket{

    private int number = 20;

    public synchronized void sale(){
        if (number > 0){
            System.out.println(Thread.currentThread().getName()+" "+number);
            number--;
        }
    }
}

Lock

public class Client {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        //Multiple threads operate on the same resource class and throw the resource class into the thread
        new Thread(()->{
            for (int i = 0; i < 20; i++) {
                ticket.sale();
            }
        }, "Zhang San").start();
        new Thread(()->{
            for (int i = 0; i < 20; i++) {
                ticket.sale();
            }
        }, "Li Si").start();
        new Thread(()->{
            for (int i = 0; i < 20; i++) {
                ticket.sale();
            }
        }, "Wang Wu").start();
    }
}

class Ticket{

    private int number = 20;
    private Lock lock = new ReentrantLock();

    public void sale(){
        lock.lock();
        try {
            if (number > 0){
                System.out.println(Thread.currentThread().getName()+" "+number);
                number--;
            }
        } finally {
            lock.unlock();
        }

    }
}

The difference between the two

  • synchronized is the java keyword and Lock is a class.
  • synchronized cannot determine the status of obtaining the lock. Lock can determine whether the lock has been obtained.
  • synchronized will automatically release the lock. Lock must release the lock manually, otherwise it will cause deadlock.
  • If thread 1 obtains a Lock but is blocked, using synchronized will cause thread 2 to wait all the time, but Lock will not.
  • synchronized is a reentrant lock, non interruptible, and unfair. Lock is a reentrant lock that can be judged and fair.
  • Synchronized is suitable for locking a small number of code synchronization problems, and Lock is suitable for locking a large number of synchronized code.

Thread communication (producer and consumer)

synchronized version

  • There is no problem using two threads
public class Client {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data.increment();
            }
        }, "plus").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data.decrement();
            }
        }, "reduce").start();
    }
}

//Waiting, business, notification
class Data{

    private int number = 0;

    public synchronized void increment(){
        if (number != 0){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        number++;
        System.out.println(Thread.currentThread().getName()+"=>"+number);
        this.notifyAll();
    }

    public synchronized void decrement(){
        if (number != 1){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        number--;
        System.out.println(Thread.currentThread().getName()+"=>"+number);
        this.notifyAll();
    }
}
  • There was a problem using four threads
public class Client {

    public static void main(String[] args) {
        Data data = new Data();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data.increment();
            }
        }, "plus").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data.decrement();
            }
        }, "reduce").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data.increment();
            }
        }, "Plus 1").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data.decrement();
            }
        }, "Minus 1").start();
    }
}
  • Use the while loop to solve the false wake-up problem.
class Data{

    private int number = 0;

    public synchronized void increment(){
        while (number != 0){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        number++;
        System.out.println(Thread.currentThread().getName()+"=>"+number);
        this.notifyAll();
    }

    public synchronized void decrement(){
        while (number != 1){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        number--;
        System.out.println(Thread.currentThread().getName()+"=>"+number);
        this.notifyAll();
    }
}

lock version

public class Client {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data.increment();
            }
        }, "plus").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data.decrement();
            }
        }, "reduce").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data.increment();
            }
        }, "Plus 1").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data.decrement();
            }
        }, "Minus 1").start();
    }
}

//Waiting, business, notification
class Data{

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

    public void increment(){
        lock.lock();
        try {
            while (number != 0){
                try {
                    condition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            number++;
            System.out.println(Thread.currentThread().getName()+"=>"+number);
            condition.signalAll();
        } finally {
            lock.unlock();
        }
    }

    public void decrement(){

        lock.lock();
        try {
            while (number != 1){
                try {
                    condition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            number--;
            System.out.println(Thread.currentThread().getName()+"=>"+number);
            condition.signalAll();
        } finally {
            lock.unlock();
        }
    }
}

condition accurately notifies and wakes up threads

public class Client {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data.printA();
            }
        }, "A").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data.printB();
            }
        }, "B").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data.printC();
            }
        }, "C").start();
    }
}

class Data{

    private Lock lock = new ReentrantLock();
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();
    private int number = 1;

    public void printA(){
        lock.lock();
        try {
            while (number != 1){
                try {
                    condition1.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName());
            number = 2;
            condition2.signal();
        } finally {
            lock.unlock();
        }
    }

    public void printB(){
        lock.lock();
        try {
            while (number != 2){
                try {
                    condition2.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName());
            number = 3;
            condition3.signal();
        } finally {
            lock.unlock();
        }
    }

    public void printC(){
        lock.lock();
        try {
            while (number != 3){
                try {
                    condition3.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName());
            number = 1;
            condition1.signal();
        } finally {
            lock.unlock();
        }
    }
}

Eight lock phenomenon

Question 1: two common synchronization methods, one object, execute thread 1 or thread 2 first?
A: thread one. The object of the synchronized lock is the caller of the method, that is, data. The two methods use the same lock. Who gets it first and who executes it.

public class Client {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(()->{
            data.thread1();
        }, "A").start();
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            data.thread2();
        }, "B").start();
    }
}

class Data{

    public synchronized void thread1(){
        System.out.println("First thread");
    }

    public synchronized void thread2(){
        System.out.println("Second thread");
    }
}

Question 2: two common synchronization methods, one object, and delay the execution of thread 1 for 4 seconds. Do you execute thread 1 or thread 2 first?
A: thread one. The object of the synchronized lock is the caller of the method, that is, data. The two methods use the same lock. Who gets it first and who executes it.

public class Client {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(()->{
            data.thread1();
        }, "A").start();
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            data.thread2();
        }, "B").start();
    }
}

class Data{

    public synchronized void thread1(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("First thread");
    }

    public synchronized void thread2(){
        System.out.println("Second thread");
    }
}

Question 3: two common synchronization methods, one common method and one object delay thread 1 for 4 seconds and thread 2 for 2 seconds. What is the execution order of threads?
A: thread 3, thread 1, thread 2. Common methods do not have locks, are not synchronous methods, and are not affected by locks.

public class Client {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(()->{
            data.thread1();
        }, "A").start();
        new Thread(()->{
            data.thread2();
        }, "B").start();
        new Thread(()->{
            data.thread3();
        }, "C").start();
    }
}

class Data{

    public synchronized void thread1(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("First thread");
    }

    public synchronized void thread2(){
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Second thread");
    }

    public void thread3(){
        System.out.println("Third thread");
    }
}

Question 4: two common synchronization methods, two objects, and delay the execution of thread 1 for 3 seconds. Do you execute thread 1 or thread 2 first?
A: thread 2. The object of the synchronized lock is the caller of the method. Two objects, two callers and two locks are executed in chronological order.

public class Client {
    public static void main(String[] args) {
        Data data1 = new Data();
        Data data2 = new Data();
        new Thread(()->{
            data1.thread1();
        }, "A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            data2.thread2();
        }, "B").start();
    }
}

class Data{

    public synchronized void thread1(){
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("First thread");
    }

    public synchronized void thread2(){
        System.out.println("Second thread");
    }
}

Question 5: two static synchronization methods, one object, and delay the execution of thread 1 for 3 seconds. Do you execute thread 1 or thread 2 first?
A: thread one. The object of the synchronized lock is the caller of the method, static is the static method, it will be available as soon as the class is loaded, and the lock is the class template.

public class Client {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(()->{
            data.thread1();
        }, "A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            data.thread2();
        }, "B").start();
    }
}

class Data{

    public static synchronized void thread1(){
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("First thread");
    }

    public static synchronized void thread2(){
        System.out.println("Second thread");
    }
}

Question 6: two static synchronization methods and two objects, and delay the execution of thread 1 for 3 seconds. Do you execute thread 1 or thread 2 first?
A: thread one. There is only one class template for the two objects, and the static lock is the class template.

public class Client {
    public static void main(String[] args) {
        Data data1 = new Data();
        Data data2 = new Data();
        new Thread(()->{
            data1.thread1();
        }, "A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            data2.thread2();
        }, "B").start();
    }
}

class Data{

    public static synchronized void thread1(){
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("First thread");
    }

    public static synchronized void thread2(){
        System.out.println("Second thread");
    }
}

Question 7: a static synchronization method, a common synchronization method, an object, and delay the execution of the static synchronization method by 3 seconds. Do you execute thread 1 or thread 2 first?
A: thread 2. The static synchronization method locks the class template. The common synchronization method locks the caller, not the same lock, and executes in chronological order.

public class Client {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(()->{
            data.thread1();
        }, "A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            data.thread2();
        }, "B").start();
    }
}

class Data{

    public static synchronized void thread1(){
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("First thread");
    }

    public synchronized void thread2(){
        System.out.println("Second thread");
    }
}

Question 8: a static synchronization method, a common synchronization method, two objects, and delay the execution of the static synchronization method by 3 seconds. Do you execute thread 1 or thread 2 first?
A: thread 2. The static synchronization method locks the class template. The common synchronization method locks the caller, not the same lock, and executes in chronological order.

public class Client {
    public static void main(String[] args) {
        Data data1 = new Data();
        Data data2 = new Data();
        new Thread(()->{
            data1.thread1();
        }, "A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            data2.thread2();
        }, "B").start();
    }
}

class Data{

    public static synchronized void thread1(){
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("First thread");
    }

    public synchronized void thread2(){
        System.out.println("Second thread");
    }
}

Collection class unsafe

List

  • Executing the following code will report Java util. Concurrentmodificationexception exception
public class ListTest {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0, 5));
                System.out.println(list);
            }, String.valueOf(i)).start();
        }
    }
}
  • Use list < string > List = collections Synchronizedlist (New ArrayList < > () solution
public class ListTest {
    public static void main(String[] args) {
        List<String> list = Collections.synchronizedList(new ArrayList<>());
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0, 5));
                System.out.println(list);
            }, String.valueOf(i)).start();
        }
    }
}
  • Use list < string > List = new copyonwritearraylist < > () (under JUC)
public class ListTest {
    public static void main(String[] args) {
    	//CopyOnWrite, copy on write, is an optimization strategy in the field of computer programming.
    	//Avoid overwriting when writing, resulting in data problems.
        List<String> list = new CopyOnWriteArrayList<>();
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0, 5));
                System.out.println(list);
            }, String.valueOf(i)).start();
        }
    }
}

Set

  • It is roughly the same as list, and java.com will be reported in common use util. Concurrentmodificationexception exception.
  • Use set < string > set = collections Synchronizedset (New HashSet < > ().
  • Use set < string > set = new copyonwritearrayset < > ().

HashMap

  • Java. Net will be reported in normal use util. Concurrentmodificationexception exception.
  • Use map < string, string > map = collections Synchronizedmap (New HashMap < > ()) solution.
  • Use map < string, string > map = new concurrenthashmap < > ().

Callable

The Callable interface is similar to Runnable. You can have a return value, throw an exception, and use the call() method.

public class CallableTest {
    public static void main(String[] args) {
        Data data = new Data();
        FutureTask<String> futureTask = new FutureTask<>(data);
        new Thread(futureTask, "123").start();
        try {
            String s = futureTask.get();
            System.out.println(s);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}

//The generic type is the return value type of the method
class Data implements Callable<String>{

    @Override
    public String call() throws Exception {
        return "123456";
    }
}

Common auxiliary classes

CountDownLatch

  • After the CountDownLatch class is used, it will wait until the six threads are executed before outputting. Every time a thread calls countDown(), the number of counters is - 1. Once the counter becomes 0, CountDownLatch Await () will wake up and continue execution.
public class CountDownLatchTest {
    public static void main(String[] args) {
        for (int i = 0; i < 6; i++) {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName());
            }, String.valueOf(i)).start();
        }
        System.out.println("========");
    }
}
public class CountDownLatchTest {
    public static void main(String[] args) {
        CountDownLatch countDownLatch = new CountDownLatch(6);
        for (int i = 0; i < 6; i++) {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName());
                countDownLatch.countDown();
            }, String.valueOf(i)).start();
        }
        try {
            countDownLatch.await();//Wait for the counter to return to zero before executing downward
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("========");
    }
}

CyclicBarrier (subtraction counter)

public class CyclicBarrierTest {
    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(6, ()->{
            System.out.println("Done.");
        });
        for (int i = 0; i < 6; i++) {
            final int temp = i;
            new Thread(()->{
                System.out.println(temp);
                try {
                    cyclicBarrier.await();
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }, String.valueOf(i)).start();
        }
    }
}

Semaphore

  • semaphore.acquire(): obtain the license. If there is no license, wait until a thread releases the license.
  • semaphore.release(): release the license and wake up the waiting thread.
  • Function: multiple shared resources are mutually exclusive! Concurrent flow restriction, control the maximum number of threads!
public class SemaphoreTest {
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(3);
        for (int i = 0; i < 6; i++) {
            new Thread(()->{
                try {
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName()+"come");
                    TimeUnit.SECONDS.sleep(3);
                    System.out.println(Thread.currentThread().getName()+"go");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    semaphore.release();
                }
            }, String.valueOf(i)).start();
        }
    }
}

Read write lock

In normal writing, it may happen that 0 is written 0, 1 is written 1, 0 is written and 1 is written.

public class ReadWriteTest {
    public static void main(String[] args) {
        Data data = new Data();
        for (int i = 0; i < 5; i++) {
            final int temp = i;
            new Thread(()->{
                data.put(temp+"", temp);
            }, String.valueOf(i)).start();
        }
        for (int i = 0; i < 5; i++) {
            final int temp = i;
            new Thread(()->{
                data.get(temp+"");
            }, String.valueOf(i)).start();
        }
    }
}

class Data{

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

    public void put(String key, Object value){
        System.out.println(Thread.currentThread().getName()+"write"+key);
        map.put(key, value);
        System.out.println(Thread.currentThread().getName()+"Finished");
    }

    public void get(String key){
        System.out.println(Thread.currentThread().getName()+"read"+key);
        map.get(key);
        System.out.println(Thread.currentThread().getName()+"Finished reading");
    }
}

Use read / write lock

public class ReadWriteTest {
    public static void main(String[] args) {
        Data data = new Data();
        for (int i = 0; i < 5; i++) {
            final int temp = i;
            new Thread(()->{
                data.put(temp+"", temp);
            }, String.valueOf(i)).start();
        }
        for (int i = 0; i < 5; i++) {
            final int temp = i;
            new Thread(()->{
                data.get(temp+"");
            }, String.valueOf(i)).start();
        }
    }
}

class Data{

    private volatile Map<String, Object> map = new HashMap<>();
    //Read write lock
    private ReadWriteLock lock = new ReentrantReadWriteLock();

    //When writing, only one thread writes
    public void put(String key, Object value){
        //Write lock
        lock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName()+"write"+key);
            map.put(key, value);
            System.out.println(Thread.currentThread().getName()+"Finished");
        } finally {
            lock.writeLock().unlock();
        }
    }

    //When reading, all threads can read
    public void get(String key){
        lock.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName()+"read"+key);
            map.get(key);
            System.out.println(Thread.currentThread().getName()+"Finished reading");
        } finally {
            lock.readLock().unlock();
        }
    }
}

Blocking queue

modeThrow exceptionIf there is a return value, no exception will be thrownBlocking waitTimeout wait
add toaddofferputoffer( , , )
removeremovepolltakepoll( , )
Judge Team Leaderelementpeek

Throw exception

    public void test1(){
        //The parameter is queue size
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println(blockingQueue.add("1"));
        System.out.println(blockingQueue.add("2"));
        System.out.println(blockingQueue.add("3"));
        System.out.println(blockingQueue.element());
		//Throw Java Lang.illegalstateexception
		//System.out.println(blockingQueue.add("4"));
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
		//Throw Java util. NoSuchElementException exception
		//System.out.println(blockingQueue.remove());
    }

If there is a return value, no exception will be thrown

    public 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"));
        System.out.println(blockingQueue.peek());
        //Returns false without throwing an exception
        //System.out.println(blockingQueue.offer("d"));
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        //Returns null without throwing an exception
        //System.out.println(blockingQueue.poll());
    }

Blocking wait

    public void test3(){
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        try {
            blockingQueue.put("a");
            blockingQueue.put("b");
            blockingQueue.put("c");
            //Keep blocking and waiting
            //blockingQueue.put("d");
            System.out.println(blockingQueue.take());
            System.out.println(blockingQueue.take());
            System.out.println(blockingQueue.take());
            //Keep blocking and waiting
            //System.out.println(blockingQueue.take());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

Timeout wait

    public void test4(){
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println(blockingQueue.offer("a"));
        System.out.println(blockingQueue.offer("b"));
        System.out.println(blockingQueue.offer("c"));
        try {
            //Wait two seconds and exit
            System.out.println(blockingQueue.offer("d", 2, TimeUnit.SECONDS));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        try {
            //Wait two seconds and exit
            System.out.println(blockingQueue.poll(2, TimeUnit.SECONDS));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

Synchronization queue

  • Synchronous queue: there is only one location. Put an element. You must take it out first, or you can't put it in again.
    public void test(){
        BlockingQueue<String> synchronousQueue = new SynchronousQueue<>();
        new Thread(()->{
            try {
                System.out.println(Thread.currentThread().getName()+"1");
                synchronousQueue.put("1");
                System.out.println(Thread.currentThread().getName()+"2");
                synchronousQueue.put("2");
                System.out.println(Thread.currentThread().getName()+"3");
                synchronousQueue.put("3");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "one").start();
        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(2);
                System.out.println(Thread.currentThread().getName()+synchronousQueue.take());
                TimeUnit.SECONDS.sleep(2);
                System.out.println(Thread.currentThread().getName()+synchronousQueue.take());
                TimeUnit.SECONDS.sleep(2);
                System.out.println(Thread.currentThread().getName()+synchronousQueue.take());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "two").start();
    }

Thread pool

  • Advantages of thread pool: thread reuse, maximum concurrency control and thread management

Three methods

public class Test1 {
    public static void main(String[] args) {
    	//Single thread
        ExecutorService service = Executors.newSingleThreadExecutor();
        //Create a fixed size thread pool
        ExecutorService service = Executors.newFixedThreadPool(5);
        //Scalable thread pool
        ExecutorService service = Executors.newCachedThreadPool();
        try {
            for (int i = 0; i < 10; i++) {
                service.execute(()->{
                    System.out.println(Thread.currentThread().getName());
                });
            }
        } finally {
            service.shutdown();
        }
    }
}

Seven parameters

	//ThreadPoolExecutor source code
	//Creating a thread is essentially a call to ThreadPoolExecutor()
    public ThreadPoolExecutor(int corePoolSize,//Core thread pool size
                              int maximumPoolSize,//Maximum thread pool size
                              long keepAliveTime,//Timeout, no call 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 strategy
        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;
    }

Four rejection strategies

  • new ThreadPoolExecutor.AbortPolicy(): if the queue is full, this thread will not be processed and an exception will be thrown.
  • new ThreadPoolExecutor.CallerRunsPolicy(): the queue is full. This thread will not be processed, but will be processed by the original thread.
  • new ThreadPoolExecutor.DiscardPolicy(): if the queue is full, the task will be lost and no exception will be thrown.
  • new ThreadPoolExecutor.DiscardOldestPolicy(): when the queue is full, try to compete with the earliest without throwing exceptions.

Custom thread pool

public class Test2 {
    public static void main(String[] args) {
        ExecutorService service = new ThreadPoolExecutor(
                2,
                Runtime.getRuntime().availableProcessors(),
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());
        try {
            for (int i = 0; i < 8; i++) {
                service.execute(()->{
                    System.out.println(Thread.currentThread().getName());
                });
            }
        } finally {
            service.shutdown();
        }
    }
}

How is the maximum thread defined?

  • CPU intensive: several core CPUs are defined as several, which can maintain the highest CPU efficiency. Use runtime getRuntime(). Availableprocessors() gets the number of computer CPU cores.
  • IO intensive: determine the number of threads that consume IO in the program. Greater than this number.

Four functional interfaces

  • Functional interface: an interface with only one method. It can be simplified with Lambda.

Function functional interface

    public void test1(){
        //The first generic type is the incoming parameter type, and the second generic type is the return value type
        Function<String, String> function = (str)->{return str;};
        System.out.println(function.apply("123"));
    }

Predicate type interface

    public void test2(){
        //The generic type is the incoming parameter type, and the return value is Boolean
        Predicate<String> predicate = (str)->{return str.isEmpty();};
        System.out.println(predicate.test("123"));
    }

Consumer consumer interface

    public void test(){
        //Only input, no return value
        Consumer<String> consumer = (str)->{System.out.println(str);};
        consumer.accept("123");
    }

Supplier supply interface

    public void test4(){
        //There are no parameters, only return values
        Supplier<String> supplier = ()->{return "123";};
        System.out.println(supplier.get());
    }

Stream flow calculation

Filter one of five users

  • id is even
  • Older than 23
  • User name to uppercase
  • User names are sorted backwards
  • Output only one user
public class User {

    private int id;
    private String name;
    private int age;

    public User(int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}
public class Client {
    public static void main(String[] args) {
        User user1 = new User(1, "a", 21);
        User user2 = new User(2, "b", 22);
        User user3 = new User(3, "c", 23);
        User user4 = new User(4, "d", 24);
        User user5 = new User(5, "e", 25);
        //The collection is responsible for storage
        List<User> list = Arrays.asList(user1, user2, user3, user4, user5);
        //Calculation of delivery flow
        //Use Lambda expression, chain programming, functional interface and Stream flow calculation
        list.stream()
                .filter((user)->{return user.getId()%2 == 0;})
                .filter((user)->{return user.getAge() > 23;})
                .map((user)->{return user.getName().toUpperCase();})
                .sorted((u1, u2)->{return u2.compareTo(u1);})
                .limit(1)
                .forEach(System.out::println);

    }
}

ForkJoin

  • ForkJoin: when there is a large amount of data, tasks are executed in parallel to improve efficiency. Features: work theft.
public class Test {
    public static void main(String[] args) {
        test1();
        test2();
        test3();
    }

    //Common practice
    public static void test1(){
        long start = System.currentTimeMillis();
        long sum = 0L;
        for (long i = 0L; i <= 10_0000_0000L; i++) {
            sum +=i;
        }
        long end = System.currentTimeMillis();
        System.out.println(sum);
        System.out.println("Program execution time:"+(end - start));
    }

    //ForkJoin
    public static void test2(){
        long start = System.currentTimeMillis();
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ForkJoinTask<Long> data = new Data(0L, 10_0000_0000L);
        ForkJoinTask<Long> submit = forkJoinPool.submit(data);
        try {
            System.out.println(submit.get());
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
        long end = System.currentTimeMillis();
        System.out.println("Program execution time:"+(end - start));
    }

    //Stream stream
    public static void test3(){
        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);
        System.out.println("Program execution time:"+(end - start));
    }
}

class Data extends RecursiveTask<Long> {

    private long start;
    private long end;
    private long temp = 100000L;

    public Data(long start, long end) {
        this.start = start;
        this.end = end;
    }

    @Override
    public Long compute() {
        if ((end - start) > 0){
            long sum = 0;
            for (long i = start; i < end; i++) {
                sum +=i;
            }
            return sum;
        } else {
            long middle = (end + start) / 2;
            Data task1 = new Data(start, middle);
            task1.fork();//Push task into thread queue
            Data task2 = new Data(middle + 1, end);
            task2.fork();
            return task1.join() + task2.join();
        }
    }
}

Asynchronous callback

    //Asynchronous callback with no return value
    public static void test1(){
        CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(()->{
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName());
        });
        System.out.println("===================");
        try {
            completableFuture.get();
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }

    //Callback with return value
    public static void test2(){
        CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(()->{
            System.out.println(Thread.currentThread().getName());
            return 123;
        });
        try {
            System.out.println(completableFuture.whenComplete((t, u) -> {//Successful callback
                System.out.println("t->"+t);
                System.out.println("u->"+u.getMessage());
            }).exceptionally((e) -> {//Failed callback
                System.out.println(e.getMessage());
                return 456;
            }).get());
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }

JMM

  • java memory model is a nonexistent thing, a concept and a convention.

Some synchronization conventions of JMM:

  • Before the thread is unlocked, the shared variables in the working memory must be immediately flushed back to the main memory.
  • Before locking a thread, the latest value in main memory must be read into working memory.
  • Locking and unlocking are the same lock.

Eight memory interactions

  • 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: 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.

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 must be loaded and store must be written.
  • 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.
  • A thread is not allowed to synchronize data without assign from working memory back to main memory.
  • A new variable must be born in main memory. Working memory is not allowed to directly use an uninitialized variable. That is, 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 that is locked by another thread
    Before unlock ing a variable, you must synchronize the variable back to main memory.

Volatile

  • Volatile is a lightweight synchronization mechanism provided by java virtual machine.

Ensure visibility

public class VolatileTest1 {

    //Without volatile, the program will loop
    //The variable value in main memory changes and is visible in thread working memory
    private volatile static int num = 0;

    public static void main(String[] args) {
        new Thread(()->{
            while (num == 0){

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

Atomicity is not guaranteed

public class VolatileTest2 {

    private volatile static int num = 0;

    public static void main(String[] args) {
        for (int i = 0; i < 20; i++) {
            new Thread(()->{
                for (int i1 = 0; i1 < 1000; i1++) {
                    num++;
                }
            }).start();
        }
        while (Thread.activeCount() > 2){
            Thread.yield();
        }
        //In theory, it should be 20000
        System.out.println(num);
    }
}
  • Using atomic classes to solve atomic problems
public class VolatileTest3 {

    private volatile static AtomicInteger num = new AtomicInteger();

    public static void main(String[] args) {
        for (int i = 0; i < 20; i++) {
            new Thread(()->{
                for (int i1 = 0; i1 < 1000; i1++) {
                    num.getAndIncrement();
                }
            }).start();
        }
        while (Thread.activeCount() > 2){
            Thread.yield();
        }
        System.out.println(num);
    }
}

Prohibit instruction rearrangement

  • Instruction rearrangement: the computer does not execute the program you write as you write.

Atomic reference

Atomic reference to solve ABA problem

public class Test {
    public static void main(String[] args) {
        AtomicStampedReference<Integer> reference = new AtomicStampedReference<>(1, 1);
        new Thread(()->{
            System.out.println("a1->"+reference.getStamp());
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("a2->"+reference.compareAndSet(1, 3,
                    reference.getStamp(), reference.getStamp() + 1));
            System.out.println("a2->"+reference.getStamp());
            System.out.println("a3->"+reference.compareAndSet(3, 1,
                    reference.getStamp(), reference.getStamp() + 1));
            System.out.println("a3->"+reference.getStamp());
        }, "a").start();
        new Thread(()->{
            int stamp = reference.getStamp();
            System.out.println("b1->"+stamp);
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("b2->"+reference.compareAndSet(1, 6,
                    stamp, stamp + 1));
            System.out.println("b2->"+reference.getStamp());
        }, "b").start();
    }
}

Various locks

Fair lock, unfair lock

  • Fair lock: very fair. You can't jump in line.
  • Unfair lock: it's very unfair. You can jump the queue. (all are unfair by default)

Reentrant lock

  • After you get the outside lock, you can automatically get the inside lock.

Topics: Java Concurrent Programming JUC