“This is the second day of my participation in the Gwen Challenge in November. See details: The Last Gwen Challenge in 2021”

1.ConcurrentHashMap

Why use ConcurrentHashMap:

  1. The HashMap thread is unsafe and causes data to be corrupted
  2. Using thread-safe Hashtables is inefficient

For these two reasons, ConcurrentHashMap comes into play.

Unsafe HashMap thread demonstration

A public, static collection:

public class Const {
    public static HashMap<String,String> map = new HashMap<>();
}
Copy the code

Thread that writes data to map:

public void run(a) {
        for (int i = 0; i < 500000; i++) {
            Const.map.put(this.getName() + (i + 1), this.getName() + i + 1);
        }
        System.out.println(this.getName() + "The end!);
    }
Copy the code

The test class:

public class Demo {
    public static void main(String[] args) throws InterruptedException {
        Thread1A a1 = new Thread1A();
        Thread1A a2 = new Thread1A();
        a1.setName(Thread 1 - "");
        a2.setName(Thread 2 - "");
        a1.start();
        a2.start();
        // Rest for 10 seconds to make sure both threads are finished
        Thread.sleep(1000 * 5);
        // Prints the collection size
        System.out.println("Map size:"+ Const.map.size()); }}Copy the code

Note: Two threads write 500000 key-value pairs to the same map, and the final map size should be: 1000000.

  1. Feign death
  2. abnormal
  3. Incorrect results

ConcurrentHashMap

A public, static collection:

public class Const {
    public static ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>();
}
Copy the code

Thread that writes data to map:

public void run(a) {
        long start = System.currentTimeMillis();
        for (int i = 0; i < 500000; i++) {
            Const.map.put(this.getName() + (i + 1), this.getName() + i + 1);
        }
        long end = System.currentTimeMillis();
        System.out.println(this.getName() + "The end! Available:" + (end - start) + "Ms");
    }
Copy the code

The test class:

public class Demo {
    public static void main(String[] args) throws InterruptedException {
        Thread1A a1 = new Thread1A();
        Thread1A a2 = new Thread1A();
        a1.setName(Thread 1 - "");
        a2.setName(Thread 2 - "");
        a1.start();
        a2.start();
        // Rest for 10 seconds to make sure both threads are finished
        Thread.sleep(1000 * 5);
        // Prints the collection size
        System.out.println("Map size:"+ Const.map.size()); }}Copy the code

Execution Result:

ConcurrentHashMap compares with HashTable

ConcurrentHashMap and HashTable are thread-safe, but their implementation mechanisms are different and their efficiency is different. The main differences are as follows:

HashTable ConcurrentHashMap (before JDK8) ConcurrentHashMap
Lock all get and PUT methods to synchronize The data is segmented and each segment is locked separately to achieve synchronization and improve efficiency CAS algorithm is adopted to improve efficiency

Reasons for low efficiency of HashTable:

public synchronized V put(K key, V value) 
public synchronized V get(Object key)
Copy the code

The HashTable container uses synchronized to ensure thread-safety, but HashTable is very inefficient in the context of competitive threads. Because when one thread accesses the synchronization method of HashTable, other threads also access the synchronization method of HashTable and block. For example, thread 1 uses PUT to add elements, thread 2 cannot use either put method to add elements or GET method to obtain elements. Therefore, the more fierce the competition, the lower the efficiency.

The reason why ConcurrentHashMap is efficient is as follows: CAS + synchronized segmented lock

Why isn’t HashMap thread safe?

When a HashMap is expanded, the elements are rearranged (the elements in the same bucket are inserted using the head method, and the original list order is inverted), and concurrent scenarios may form circular lists.

Why is ConcurrentHashMap not a complete replacement for HashTable

Because ConcurrentHashMap is weakly consistent, its GET method is not locked. As a result, the GET element is not the value of the current parallel and unfinished PUT, and the read data is not necessarily the final value, which may cause errors in some scenarios requiring strong consistency. For example, you need to check whether the current value is A. If the value is not A, change it to C. However, the current value is B and A PUT method is used to change the value to A.

2.CountDownLatch

1. An overview of the

CountDownLatch allows one or more threads to wait for other threads to complete before executing themselves.

For example: thread 1 to perform printing: A and C, thread 2 to perform print: B, but the thread 1, after printing A thread to print after B to print 2 C, so: thread 1, after printing A after 2 print B must wait for thread to continue.

CountDownLatch constructor

public CountDownLatch(int count)// Initialize a CountDownLatch object with the specified counter
Copy the code

CountDownLatch important methods:

public void await(a) throws InterruptedException// Make the current thread wait
public void countDown(a) // The counter is decrement by 1
Copy the code

Example 2.

  1. Thread 1

    public class ThreadA extends Thread {
        private CountDownLatch down;
        public ThreadA(CountDownLatch down) {
            this.down = down;
        }
        @Override
        public void run(a) {
            System.out.println("A");
            try {
                down.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("C"); }}Copy the code
  2. Thread 2

    public class ThreadB extends Thread {
        private CountDownLatch down;
        public ThreadB(CountDownLatch down) {
            this.down = down;
        }
        @Override
        public void run(a) {
            System.out.println("B"); down.countDown(); }}Copy the code
  3. The test class

    public class Demo {
        public static void main(String[] args) {
            CountDownLatch down = new CountDownLatch(1);// Create a counter
            new ThreadA(down).start();
            newThreadB(down).start(); }}Copy the code
  4. The execution results are printed in the following order: A, B, and C.

CountDownLatch is supported by a counter. Every time a thread completes its task, the CountDownLatch () method is called to count -1, and CountDownLatch is invoked when the counter reaches 0. The thread block of the await() method is removed and execution continues.

3.CyclicBarrier

1. An overview of the

CyclicBarrier literally means CyclicBarrier. What it does is allow a group of threads to block when they reach a barrier (also known as a synchronization point), and the barrier will not open until the last thread reaches the barrier, and all threads blocked by the barrier will continue to run.

For example, the company calls a meeting of five employees. When all five employees arrive, the meeting begins.

We create 5 employee threads and 1 meeting thread, start almost at the same time, use CyclicBarrier to ensure that all 5 employee threads execute, and then execute the meeting thread.

CyclicBarrier construction method:

public CyclicBarrier(int parties, Runnable barrierAction)// It is used to preferentially execute barrierAction when the thread reaches the barrier for more complex business scenarios
Copy the code

CyclicBarrier important methods:

public int await(a)// Each thread calls the await method to tell the CyclicBarrier that I have reached the barrier, and the current thread is blocked
Copy the code

Example 2.

  1. Employees thread
public class PersonThread extends Thread {
    private CyclicBarrier cbRef;
    public PersonThread(CyclicBarrier cbRef) {
        this.cbRef = cbRef;
    }
    @Override
    public void run(a) {
        try {
            Thread.sleep((int) (Math.random() * 1000));
            System.out.println(Thread.currentThread().getName() + "Here! ");
            cbRef.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch(BrokenBarrierException e) { e.printStackTrace(); }}}Copy the code
  1. Thread the meeting
public class MeetingThread extends Thread {
    @Override
    public void run(a) {
        System.out.println("Ok, people are here, start meeting......"); }}Copy the code
  1. The test class
public class Demo {
    public static void main(String[] args) {
        CyclicBarrier cbRef = new CyclicBarrier(5.new MeetingThread());// Wait for all five threads to complete before executing MeetingThread
        PersonThread p1 = new PersonThread(cbRef);
        PersonThread p2 = new PersonThread(cbRef);
        PersonThread p3 = new PersonThread(cbRef);
        PersonThread p4 = new PersonThread(cbRef);
        PersonThread p5 = newPersonThread(cbRef); p1.start(); p2.start(); p3.start(); p4.start(); p5.start(); }}Copy the code
  1. The execution result

3. Application scenarios

Usage scenario: CyclicBarrier can be used in scenarios where multiple threads compute data and then combine the results.

Requirement: use two threads to read the data in two files. When the data in two files are read, the data is summarized.

4.Semaphore

1. An overview of the

The main function of Semaphore is to control the number of concurrent threads.

Synchronized acts as a “lock,” but only one thread is allowed to execute at a time.

Semaphore can be set up to allow several threads to execute simultaneously.

Semaphore literally means Semaphore, which controls the number of threads accessing a particular resource.

Semaphore constructor:

public Semaphore(int permits)              // Permits indicates the number of permitting threads
public Semaphore(int permits, boolean fair) //fair indicates fairness. If this is set to true, the next thread to execute will be the one that has waited the longest
Copy the code

Semaphore Important methods:

public void acquire(a) throws InterruptedException   // Indicates that the license is obtained
public void release(a)                             //release() indicates the release permission
Copy the code

Example 2.

Example 1: Allow one thread to execute simultaneously

  1. The Service class

    public class Service {
        private Semaphore semaphore = new Semaphore(1);//1 indicates permission, meaning that at most one thread is allowed to execute the contents between acquire() and release()
        public void testMethod(a) {
            try {
                semaphore.acquire();
                System.out.println(Thread.currentThread().getName()
                        + "Entry time =" + System.currentTimeMillis());
                Thread.sleep(1000);
                System.out.println(Thread.currentThread().getName()
                        + "End time =" + System.currentTimeMillis());
                semaphore.release();
                // The code between acquire() and release() is "sync code"
            } catch(InterruptedException e) { e.printStackTrace(); }}}Copy the code
  2. Thread class

    public class ThreadA extends Thread {
        private Service service;
        public ThreadA(Service service) {
            super(a);this.service = service;
        }
        @Override
        public void run(a) { service.testMethod(); }}Copy the code
  3. The test class

    public class Demo {
        public static void main(String[] args) {
            Service service = new Service();
            // Start 5 threads
            for (int i = 1; i <= 5; i++) {
                ThreadA a = new ThreadA(service);
                a.setName("Thread" + i);
                a.start();The testMethod method of the Service can be executed simultaneously by 5 threads, and only 1 thread can execute it at any given time}}}Copy the code
  4. The results of

Example 2: Allow two threads to execute simultaneously

  1. Change new Semaphore(1) to 2

    public class Service {
        private Semaphore semaphore = new Semaphore(2);//2 indicates permission, meaning that up to 2 threads are allowed to execute contents between acquire() and release()
        public void testMethod(a) {
            try {
                semaphore.acquire();
                System.out.println(Thread.currentThread().getName()
                        + "Entry time =" + System.currentTimeMillis());
                Thread.sleep(5000);
                System.out.println(Thread.currentThread().getName()
                        + "End time =" + System.currentTimeMillis());
                semaphore.release();
                // The code between acquire() and release() is "sync code"
            } catch(InterruptedException e) { e.printStackTrace(); }}}Copy the code
  2. The results of

5.Exchanger

An overview of the

Sanoer is a tool class used for collaboration between threads. Used for data exchange between threads.

The two threads exchange data using the Exchange method. If the first thread executes the Exchange () method first, it will wait until the second thread executes the exchange method. When both threads reach the synchronization point, the two threads can exchange data, passing the data produced by each thread to the other.

Sanostructural method:

public Exchanger(a)
Copy the code

Non-recovery Important methods:

public V exchange(V x)
Copy the code

The sample

Example 1: The blocking nature of the Exchange method

  1. Thread A and is able to receive A SANo1100Object

    public class ThreadA extends Thread {
        private Exchanger<String> exchanger;
        public ThreadA(Exchanger<String> exchanger) {
            super(a);this.exchanger = exchanger;
        }
        @Override
        public void run(a) {
            try {
                System.out.println("Thread A wants to pass the value 'gift A' to thread B and wait for the value of thread B...");
                System.out.println("Get the value of thread B in thread A =" + exchanger.exchange("A gift"));
            } catch(InterruptedException e) { e.printStackTrace(); }}}Copy the code
  2. The test class

    public class Demo {
        public static void main(String[] args) {
            Exchanger<String> exchanger = new Exchanger<String>();
            ThreadA a = newThreadA(exchanger); a.start(); }}Copy the code
  3. The results of

Example 2: The Exchange method performs the exchange

  1. Thread A

    public class ThreadA extends Thread {
        private Exchanger<String> exchanger;
        public ThreadA(Exchanger<String> exchanger) {
            super(a);this.exchanger = exchanger;
        }
        @Override
        public void run(a) {
            try {
                System.out.println("Thread A wants to pass the value 'gift A' to thread B and wait for the value of thread B...");
                System.out.println("Get the value of thread B in thread A =" + exchanger.exchange("A gift"));
            } catch(InterruptedException e) { e.printStackTrace(); }}}Copy the code
  2. Thread B

    public class ThreadB extends Thread {
        private Exchanger<String> exchanger;
        public ThreadB(Exchanger<String> exchanger) {
            super(a);this.exchanger = exchanger;
        }
        @Override
        public void run(a) {
            try {
                System.out.println("Thread B wants to pass the value 'gift B' to thread A and wait for the value of thread A...");
                System.out.println("Get the value of thread A in thread B =" + exchanger.exchange("B"));
    ​
            } catch(InterruptedException e) { e.printStackTrace(); }}}Copy the code
  3. The test class

    public class Demo {
        public static void main(String[] args) throws InterruptedException {
            Exchanger<String> exchanger = new Exchanger<String>();
            ThreadA a = new ThreadA(exchanger);
            ThreadB b = newThreadB(exchanger); a.start(); b.start(); }}Copy the code
  4. The results of

Example 3: Timeout of the Exchange method

  1. Thread A

    public class ThreadA extends Thread {
        private Exchanger<String> exchanger;
        public ThreadA(Exchanger<String> exchanger) {
            super(a);this.exchanger = exchanger;
        }
        @Override
        public void run(a) {
            try {
                System.out.println("Thread A wants to pass the value 'gift A' to thread B and wait for the value of thread B for 5 seconds...");
                System.out.println("Get the value of thread B in thread A =" + exchanger.exchange("A gift".5, TimeUnit.SECONDS));
                System.out.println("Thread A terminated!");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (TimeoutException e) {
                System.out.println("Thread A ends without waiting for the value of thread B for 5 seconds!"); }}}Copy the code
    1. The test class

      public class Run {
          public static void main(String[] args) {
              Exchanger<String> exchanger = new Exchanger<String>();
              ThreadA a = newThreadA(exchanger); a.start(); }}Copy the code
    2. The results of