JUC (2. Interthread Communication)

Posted by Solar on Mon, 05 Aug 2019 06:01:29 +0200

Two threads, one printing 1-52, the other printing alphabet A-Z printing order is 12134B... 5152Z,
Require inter-thread communication

1.synchronized Implementation

package com.liuyuanyuan.thread;
 
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
 
import org.omg.IOP.Codec;
 
 
class ShareDataOne//Resource class
{
  private int number = 0;//A variable with an initial value of zero
 
  public synchronized void increment() throws InterruptedException 
  {
     //1 Judgment
     if(number !=0 ) {
       this.wait();
     }
     //2 Work
     ++number;
     System.out.println(Thread.currentThread().getName()+"\t"+number);
     //3 Notice
     this.notifyAll();
  }
  
  public synchronized void decrement() throws InterruptedException 
  {
     // 1 Judgment
     if (number == 0) {
       this.wait();
     }
     // 2 Work
     --number;
     System.out.println(Thread.currentThread().getName() + "\t" + number);
     // 3 Notice
     this.notifyAll();
  }
}
 
/**
 * 
 * @Description:
 *Now two threads,
 * A variable with an initial value of zero can be manipulated.
 * Implement a thread to add 1 to the variable and a thread to subtract 1 from the variable.
 * Alternately, 10 rounds. 
 * @author liuyuan
 *
 *  * Note: How to write multi-threaded at the engineering level in Java
 * 1 Multithreading becomes a template (routine) - - on
 *     1.1  Thread Operations Resource Class  
 *     1.2  High cohesion and low coupling
 * 2 Multithreading becomes a template (routine) - - below
 *     2.1  judge
 *     2.2  work
 *     2.3  notice
 
 */
public class NotifyWaitDemoOne
{
  public static void main(String[] args)
  {
     ShareDataOne sd = new ShareDataOne();
     new Thread(() -> {
       for (int i = 1; i < 10; i++) {
          try {
            sd.increment();
          } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
          }
       }
     }, "A").start();
     new Thread(() -> {
       for (int i = 1; i < 10; i++) {
          try {
            sd.decrement();
          } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
          }
       }
     }, "B").start();
  }
}
/*
 * * 
 * 2 Multithreading becomes a template (routine) - - below
 *     2.1  judge
 *     2.2  work
 *     2.3  notice
 * 3 Use while to prevent false Awakening
 * 
 * 
 * */
 
 
 

Replacing with four threads can lead to errors and false Awakening
 
Reason: if can't be used in java multi-threaded judgment, the program accident happened in judgment.
Suddenly an additional thread enters if and suddenly interrupts the handover of control.
Instead of validation, they went straight on, adding two or more times.

For example, in life, waiting for an airplane in the airport, if some problems can not take off, the airport arranged for a temporary rest in the hotel, after solving the problem, we still need to re-check when we return to the airport.

Resolve false wake-up: View API, java.lang.Object

 
Interruption and false wake-up are possible, so use loop loop, if only to judge once, while is to pull back and judge again as long as wake-up. Change if to while

The above is achieved with synchronized

Here's a better implementation of jdk1.8

The idea is simple.

Next comes the concrete code implementation.

Condition: See API,java.util.concurrent

 
class BoundedBuffer {
   final Lock lock = new ReentrantLock();
   final Condition notFull  = lock.newCondition(); 
   final Condition notEmpty = lock.newCondition(); 
 
   final Object[] items = new Object[100];
   int putptr, takeptr, count;
 
   public void put(Object x) throws InterruptedException {
     lock.lock();
     try {
       while (count == items.length)
         notFull.await();
       items[putptr] = x;
       if (++putptr == items.length) putptr = 0;
       ++count;
       notEmpty.signal();
     } finally {
       lock.unlock();
     }
   }
 

2. Interthreaded customized call communication

1. Sequential notifications require identification bits
 
2. There is a lock Lock and three key conditions.
 
3. Judging Sign Location
 
4. Output Thread Name + Number of Numbers + Number of Rounds
 
5. Modify the flag and notify the next one
 
The above may be obscure, but let's look at the specific code implementation

package com.liuyuanyuan.thread;
 
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
 
 
class ShareResource
{
  private int number = 1;//1:A 2:B 3:C 
  private Lock lock = new ReentrantLock();
  private Condition c1 = lock.newCondition();
  private Condition c2 = lock.newCondition();
  private Condition c3 = lock.newCondition();
 
  public void print5(int totalLoopNumber)
  {
     lock.lock();
     try 
     {
       //1 Judgment
       while(number != 1)
       {
          //A Will Stop
          c1.await();
       }
       //2 Work
       for (int i = 1; i <=5; i++) 
       {
          System.out.println(Thread.currentThread().getName()+"\t"+i+"\t totalLoopNumber: "+totalLoopNumber);
       }
       //3 Notice
       number = 2;
       c2.signal();
     } catch (Exception e) {
       e.printStackTrace();
     } finally {
       lock.unlock();
     }
  }
  public void print10(int totalLoopNumber)
  {
     lock.lock();
     try 
     {
       //1 Judgment
       while(number != 2)
       {
          //A Will Stop
          c2.await();
       }
       //2 Work
       for (int i = 1; i <=10; i++) 
       {
          System.out.println(Thread.currentThread().getName()+"\t"+i+"\t totalLoopNumber: "+totalLoopNumber);
       }
       //3 Notice
       number = 3;
       c3.signal();
     } catch (Exception e) {
       e.printStackTrace();
     } finally {
       lock.unlock();
     }
  }  
  
  public void print15(int totalLoopNumber)
  {
     lock.lock();
     try 
     {
       //1 Judgment
       while(number != 3)
       {
          //A Will Stop
          c3.await();
       }
       //2 Work
       for (int i = 1; i <=15; i++) 
       {
          System.out.println(Thread.currentThread().getName()+"\t"+i+"\t totalLoopNumber: "+totalLoopNumber);
       }
       //3 Notice
       number = 1;
       c1.signal();
     } catch (Exception e) {
       e.printStackTrace();
     } finally {
       lock.unlock();
     }
  }  
}
 
 
/**
 * 
 * @Description: 
 * Multi-threads are called sequentially to achieve A->B->C
 * Three threads are started with the following requirements:
 * 
 * AA Print 5 times, BB print 10 times, CC print 15 times
 * Next
 * AA Print 5 times, BB print 10 times, CC print 15 times
 * ......10 rounds  
 * @author liuyuan
 *
 */
public class ThreadOrderAccess
{
  public static void main(String[] args)
  {
     ShareResource sr = new ShareResource();
     
     new Thread(() -> {
       for (int i = 1; i <=10; i++) 
       {
          sr.print5(i);
       }
     }, "AA").start();
     new Thread(() -> {
       for (int i = 1; i <=10; i++) 
       {
          sr.print10(i);
       }
     }, "BB").start();
     new Thread(() -> {
       for (int i = 1; i <=10; i++) 
       {
          sr.print15(i);
       }
     }, "CC").start();   
     
     
  }
}
 
 

3.NotSafeDemo

Requirement: Please give an example to illustrate that collection classes are insecure

java.util.ConcurrentModificationException


If ArrayList is modified at the same time during iteration, it will
Throw a java.util.ConcurrentModificationException exception
Concurrent modification exception

List<String> list = new ArrayList<>();
for (int i = 0; i <30 ; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,8));
                System.out.println(list);
            },String.valueOf(i)).start();
        }
 
//Look at the source code for ArrayList
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}
//Insecurity without synchronized threads

How to solve it?

List<String> list = new Vector<>();

//Look at Vector's source code
public synchronized boolean add(E e) {
    modCount++;
    ensureCapacityHelper(elementCount + 1);
    elementData[elementCount++] = e;
    return true;
}
//synchronized thread security
 

List<String> list = Collections.synchronizedList(new ArrayList<>());
Collections provides the method synchronized list to ensure that lists are synchronized thread-safe
 
Is HashMap, HashSet thread-safe? Neither
So there's the same thread security approach.

 

Ultimate boss comes out: Write-time replication

List<String> list = new CopyOnWriteArrayList<>();

Next, I'll introduce Write-Time Copying.

Write-time replication solves the problem that unlocked performance improves errors and data consistency decreases.

Next, I introduce CopyOnWriteArrayList.

A thread-safe variant of ArrayList in which all mutative operations (add, set, and so on) are implemented by making a fresh copy of the underlying array.
CopyOnWriteArrayList is a thread-safe variant of arraylist.
All the variable operations (add, set, etc.) are implemented by generating new copies of the underlying array.
 

Duplicate a copy and add it to it.

 
/**
 * Appends the specified element to the end of this list.
 *
 * @param e element to be appended to this list
 * @return {@code true} (as specified by {@link Collection#add})
 */
public boolean add(E e) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        Object[] elements = getArray();
        int len = elements.length;
        Object[] newElements = Arrays.copyOf(elements, len + 1);
        newElements[len] = e;
        setArray(newElements);
        return true;
    } finally {
        lock.unlock();
    }
}
 
 
 
CopyOnWrite A container is a container that is copied at write time. When adding elements to a container, do not go directly to the current container Object[]Add to,
//Instead, the current container Object [] is copied, a new container Object[] newElements is copied, and then elements are added to the new container Object[] newElements.
//After adding the element, point the reference to the original container to the new container setArray (new Elements).
//The advantage of this is that the CopyOnWrite container can be read concurrently without locking, because the current container does not add any elements.
//So the CopyOnWrite container is also an idea of separation of read and write, reading and writing different containers.
 
 

Do you have write-time replication technology for set and map?

Yes, of course.

Set<String> set = new HashSet<>();//Thread insecurity
 

Set<String> set = new CopyOnWriteArraySet<>();//Thread Safety
 
 
 
HashSet What is the underlying data structure?
HashMap  ?
 
//But HashSet add s a value, and HashMap puts K and V key-value pairs.
 
 
 
 
 
 
public HashSet() {
    map = new HashMap<>();
}
 
private static final Object PRESENT = new Object();
 
public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}
 
 
Map<String,String> map = new HashMap<>();//Thread insecurity
 

Map<String,String> map = new ConcurrentHashMap<>();//Thread Safety
 
 
  
 
package com.liuyuanyuan.gmall.jucdemo;

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

/**
 * Please give an example to illustrate that collection classes are insecure
 */
public class NotSafeDemo {
    public static void main(String[] args) {

        Map<String,String> map = new ConcurrentHashMap<>();
        for (int i = 0; i <30 ; i++) {
            new Thread(()->{
                map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,8));
                System.out.println(map);
            },String.valueOf(i)).start();
        }


    }

    private static void setNoSafe() {
        Set<String> set = new CopyOnWriteArraySet<>();
        for (int i = 0; i <30 ; i++) {
            new Thread(()->{
                set.add(UUID.randomUUID().toString().substring(0,8));
                System.out.println(set);
            },String.valueOf(i)).start();
        }
    }

    private static void listNoSafe() {
        //        List<String> list = Arrays.asList("a","b","c");
        //        list.forEach(System.out::println);
        //Write-time replication
        List<String> list = new CopyOnWriteArrayList<>();
        // new CopyOnWriteArrayList<>();
        //Collections.synchronizedList(new ArrayList<>());
        //new Vector<>();//new ArrayList<>();

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


}





    /**
     * Write-time replication
     CopyOnWrite A container is a container that is copied at write time. When you add elements to a container, you do not add them directly to the current container Object [].
     Instead, the current container Object [] is copied, a new container Object[] newElements is copied, and then elements are added to the new container Object[] newElements.
     After adding the element, point the reference to the original container to the new container setArray (new Elements).
     The advantage of this is that the CopyOnWrite container can be read concurrently without locking, because the current container does not add any elements.
     So the CopyOnWrite container is also an idea of separation of read and write, reading and writing different containers.

     *
     *
     *
     *

    public boolean add(E e) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            newElements[len] = e;
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
    }
     */
 
 

Topics: Java codec REST