catalogue
1. List collection thread safety
3.HashMap collection thread safety
9.1 CountDownLatch decrease count
1. List collection thread safety
The list collection is thread unsafe. When multiple threads modify the list, exceptions will occur, as shown in the following code
/** * List Collection thread unsafe */ public class Test08 { public static void main(String[] args) { List<String> list = new ArrayList<>(); //Multithreading, adding elements for (int i = 0; i < 30; i++) { new Thread(()->{ //add to list.add(UUID.randomUUID().toString().substring(0,8)); //Get content ConcurrentModificationException System.out.println(list); },String.valueOf(i)).start(); } } }
1.1 Vector: less used
List<String> list = new Vector<>();
Vector uses the Synchronized keyword to solve the concurrency problem, which is not efficient
1.2 Collections tool class, less used
List<String> list = Collections.synchronizedList(new ArrayList<>());
1.3 JUC solution, common
List<String> list = new CopyOnWriteArrayList<>();
Underlying principle of CopyOnWriteArrayList:
Copy on write technology:
For a data, concurrent reading of its contents is supported during reading. When writing, it is written independently (first copy a set that is the same as the original set, write new content, and merge the new content with the previous set), and then read is to read the content in the new set.
It is explained in the JDK help document:
It is a variant of ArrayList thread safe version. A series of operations are completed by making a new copy of the underlying array. This is usually expensive. When the number of traversal operations far exceeds the modification, it may be more effective than the alternative method. It is useful when you can't or don't want to synchronize traversal, but you need to eliminate the interference between concurrent threads.
Its add method source code:
public boolean add(E e) { final ReentrantLock lock = this.lock; lock.lock(); try { Object[] elements = getArray(); int len = elements.length; //1. Array copy Object[] newElements = Arrays.copyOf(elements, len + 1); //2. Adding new elements newElements[len] = e; //3. Consolidation setArray(newElements); return true; } finally { lock.unlock(); } }
2.HashSet set thread safety
Set<String> set = new HashSet<>();
Solution:
Set<String> set = new CopyOnWriteArraySet<>();
3.HashMap collection thread safety
Map<String, String> map = new HashMap<>();
Solution:
Map<String, String> map = new ConcurrentHashMap<>();
4. Eight questions
1. Output SMS first and then email
class Phone{ public synchronized void sendSMS() throws Exception{ System.out.println("-------sendSMS"); } public synchronized void sendEmail() throws Exception{ System.out.println("------sendEmail"); } public void getHello(){ System.out.println("------getHello"); } } public class Test09 { public static void main(String[] args) throws InterruptedException { Phone p1 = new Phone(); new Thread(()->{ try { p1.sendSMS(); } catch (Exception e) { e.printStackTrace(); } },"AA").start(); //Here, sleep will lead to the final result, which must be SMS before Email Thread.sleep(100); new Thread(()->{ try { p1.sendEmail(); } catch (Exception e) { e.printStackTrace(); } },"BB").start(); } }
2. Stay in SMS for 4 seconds, still SMS first and then Email
class Phone{ public synchronized void sendSMS() throws Exception{ //Stay for 4 seconds TimeUnit.SECONDS.sleep(4); System.out.println("-------sendSMS"); } public synchronized void sendEmail() throws Exception{ System.out.println("------sendEmail"); } public void getHello(){ System.out.println("------getHello"); } } public class Test09 { public static void main(String[] args) throws InterruptedException { Phone p1 = new Phone(); //Phone p2 = new Phone(); new Thread(()->{ try { p1.sendSMS(); } catch (Exception e) { e.printStackTrace(); } },"AA").start(); Thread.sleep(100); new Thread(()->{ try { p1.sendEmail(); } catch (Exception e) { e.printStackTrace(); } },"BB").start(); } }
3. Hello before SMS
When the method in thread BB is changed to getHello, hello will be output first, and SMS will output after waiting for 4 seconds.
p1.getHello();
4. Now there are 2 mobile phones, Email first and EMS later
public static void main(String[] args) throws InterruptedException { Phone p1 = new Phone(); Phone p2 = new Phone(); new Thread(()->{ try { p1.sendSMS(); } catch (Exception e) { e.printStackTrace(); } },"AA").start(); Thread.sleep(100); new Thread(()->{ try { p2.sendEmail(); } catch (Exception e) { e.printStackTrace(); } },"BB").start(); }
5. One mobile phone, two static methods, output SMS and Email
class Phone{ public static synchronized void sendSMS() throws Exception{ //Stay for 4 seconds TimeUnit.SECONDS.sleep(4); System.out.println("-------sendSMS"); } public static synchronized void sendEmail() throws Exception{ System.out.println("------sendEmail"); } public void getHello(){ System.out.println("------getHello"); } } public class Test09 { public static void main(String[] args) throws InterruptedException { Phone p1 = new Phone(); Phone p2 = new Phone(); new Thread(()->{ try { p1.sendSMS(); } catch (Exception e) { e.printStackTrace(); } },"AA").start(); Thread.sleep(100); new Thread(()->{ try { p1.sendEmail(); } catch (Exception e) { e.printStackTrace(); } },"BB").start(); } }
5. Fair lock and unfair lock
Fair lock: multiple threads obtain locks in the order of applying for locks. Threads will directly enter the queue to queue. They will always be the first in the queue to obtain locks.
- Advantages: all threads can get resources and will not starve to death in the queue.
- Disadvantages: the throughput will drop a lot. Except for the first thread in the queue, other threads will block, and the cost of cpu waking up blocked threads will be great.
Unfair lock: when multiple threads acquire a lock, they will directly try to obtain it. If they cannot obtain it, they will enter the waiting queue. If they can obtain it, they will directly obtain the lock.
- Advantages: it can reduce the overhead of CPU wake-up threads, and the overall throughput efficiency will be high. The CPU does not have to wake up all threads, which will reduce the number of wake-up threads.
- Disadvantages: you may also find that this may cause the thread in the middle of the queue to fail to obtain the lock all the time or fail to obtain the lock for a long time, resulting in starvation.
//true means fair lock private final ReentrantLock reentrantLock = new ReentrantLock(true);
protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (!hasQueuedPredecessors() && //Is someone here? compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { //People line up int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; }
6. Re entrant lock
Both synchronized (implicit) and lock (explicit) are reentrant locks
Implicit: the locking and unlocking process is completed automatically
synchronized reentrant locks, also known as recursive locks, can be called recursively
7. Deadlock
Deadlock: a phenomenon in which two or more processes wait for each other because they compete for resources during execution. If there is no external interference, they can no longer be executed.
Causes of Deadlock: 1 Insufficient system resources, 2 The process running sequence is inappropriate, 3 Improper allocation of resources
Handwritten simple deadlock Code:
/** * DeadLock demonstration */ public class Test10 { static Object a = new Object(); static Object b = new Object(); public static void main(String[] args) { new Thread(()->{ synchronized(a){ System.out.println("Hold lock A:"+a); synchronized (b){ System.out.println("Acquire lock b"); } } },"AA").start(); new Thread(()->{ synchronized(b){ System.out.println("Hold lock B:"+b); synchronized (a){ System.out.println("Acquire lock a"); } } },"BB").start(); } }
Deadlock verification method:
Open the console
Enter jps to find the process number of the current class, and enter jstack to query the stack information
Check stack information
8.Callable interface
The difference between Runnable and Callable:
1. The method specified by Callable is call(), and the method specified by runnable is run()
2. Callable tasks can return values after execution, while Runnable tasks cannot return values
3. The call method can throw exceptions, but the run method cannot
4. Running the Callable task can get a Future object that represents the result of asynchronous calculation. It provides a method to check whether the calculation is completed, wait for the calculation to complete, and retrieve the results of the calculation. Through the Future object, you can understand the task execution, cancel the task execution, and obtain the execution results.
FutureTask
Easy to use:
public static void main(String[] args) throws ExecutionException, InterruptedException { FutureTask<Integer> futureTask = new FutureTask<>(() -> { return 1024; }); new Thread(futureTask,"aa thread ").start(); System.out.println("Value:"+futureTask.get()); System.out.println("main end"); }
9.JUC auxiliary class
9.1 CountDownLatch decrease count
After six students left the classroom one after another, the monitor locked the door.
/** * Show me CountDownLatchDemo */ public class Test12 { //After six students left the classroom one after another, the monitor locked the door public static void main(String[] args) { //Create 6 threads for (int i = 1; i <=6; i++) { new Thread(()->{ System.out.println(Thread.currentThread().getName() + "Left the classroom"); },String.valueOf(i)).start(); } //Output shift leader locks the door System.out.println(Thread.currentThread().getName() + ": The door is locked"); } }
But there are problems:
The monitor didn't wait for all six students to leave, but at least one student locked it before going out.
solve:
//After six students left the teacher one after another, the monitor locked the door public static void main(String[] args) throws InterruptedException { //CountDownLatch counter CountDownLatch countDownLatch = new CountDownLatch(6); //Create 6 threads for (int i = 1; i <=6; i++) { new Thread(()->{ System.out.println(Thread.currentThread().getName() + "Left the classroom"); countDownLatch.countDown(); },String.valueOf(i)).start(); } countDownLatch.await(); System.out.println(Thread.currentThread().getName() + ": The door is locked"); }
9.2 CyclicBarrier
Introduction: it can block a group of threads until an event occurs, and it can block a group of threads until an event occurs. CyclicBarrier: it can make a certain number of threads gather repeatedly at the fence position. When the thread reaches the fence position, the await method will be called, which will block until all threads reach the fence position. If all threads reach the fence position, the fence will open, all threads will be released, and the fence will be reset for next use.
Construction method:
The default construction method of CyclicBarrier is CyclicBarrier(int parties). Its parameter indicates the number of threads intercepted by the barrier. Each thread uses the await() method to tell CyclicBarrier that I have reached the barrier, and then the current thread is blocked.
Another constructor of CyclicBarrier, CyclicBarrier(int parties, Runnable barrierAction), is used to give priority to the execution of barrierAction when a thread reaches the barrier, so as to facilitate the processing of more complex business scenarios.
await method:
The thread calling the await method tells the CyclicBarrier that it has reached the synchronization point, and then the current thread is blocked
Code demonstration:
/** * Demonstrate CyclicBarrier */ public class Test13 { //Create fixed value private static final int NUMBER = 7; //Collect seven dragon balls to summon the Dragon public static void main(String[] args) { //Create CyclicBarrier CyclicBarrier cyclicBarrier = new CyclicBarrier(NUMBER,()->{ System.out.println("Collect 7 dragon balls to summon the dragon"); }); //Process of collecting 7 dragon balls for (int i = 1; i <= 7; i++) { new Thread(()->{ try{ System.out.println(Thread.currentThread().getName() + "The star dragon balls have been collected"); //wait for cyclicBarrier.await(); }catch (Exception e){ e.printStackTrace(); } },String.valueOf(i)).start(); } } }
The difference between CyclicBarrier and CountDownLatch:
The counter of CountDownLatch can only be used once, while the counter of CyclicBarrier can be reset with the reset() method and can be used multiple times, so CyclicBarrier can handle more complex scenes;
CyclicBarrier also provides some other useful methods. For example, getNumberWaiting() method can obtain the number of threads blocked by CyclicBarrier, and isBroken() method is used to know whether the blocked thread is interrupted;
CountDownLatch allows one or more threads to wait for the generation of a set of events, while CyclicBarrier is used to wait for other threads to run to the fence
9.3 Semaphore semaphore
Introduction:
Semaphore. Conceptually, semaphores maintain a set of licenses, which can also be understood as the use rights of a set of resources. We can use the acquire method to obtain the license, return the license, and use the release method. Semaphores are often used to limit the number of threads for certain resources. For example, limit the concurrency of a web interface.
The process of semaphore releasing resources:
The application layer calls the release method to release resources
Tryrereleaseshared attempted to free the resource
If the resource is released successfully, wake up the next node
Code demonstration:
/** * Demo Semaphore */ public class Test14 { //6 cars, 3 parking spaces public static void main(String[] args) { //Create semaphore Semaphore semaphore = new Semaphore(3); //6 vehicles for (int i = 0; i < 6; i++) { new Thread(()->{ try{ //obtain semaphore.acquire(); System.out.println(Thread.currentThread().getName()+"Grabbed the parking space"); //Simulated parking time TimeUnit.SECONDS.sleep(new Random().nextInt(5)); System.out.println(Thread.currentThread().getName()+"--Left the parking space"); }catch (Exception e){ e.printStackTrace(); }finally { //release semaphore.release(); } },String.valueOf(i)).start(); } } }