Diligence can make up for the lack of intelligence, but intelligence can not make up for the defect of laziness. Hello, I’m Mengyangchen! Looking forward to meeting you!
The article directories
-
An overview of the
-
01 Java multithreading review
-
wait/sleep
-
02. Producer and consumer review
-
03. New version of producer and consumer writing (using Lock,Condition)
-
Advantages of the new writing method
-
Lock 04.
-
The list thread is not safe to handle
-
06. The set thread is not handled safely
-
07. The map thread is not processed safely
An overview of the
JUC stands for java.util.Concurrent toolkit, commonly known as Java and package issuing. This is a toolkit for working with threads, which started with JDK 1.5.
01 Java multithreading review
Interface Lock
The Lock implementation provides a wider range of locking operations than can be obtained using synchronized methods and statements. They allow for more flexible structuring, may have completely different attributes, and can support multiple related objects, conditions.
Locks are tools for controlling access to shared resources across multiple threads. Typically, locks provide exclusive access to a shared resource: only one thread can acquire the lock at a time, and all access to the shared resource requires the lock to be acquired first. However, some locks may allow concurrent access to shared resources, such as the read lock for ReadWriteLock.
Lock l = ... ; l.lock(); try { // access the resource protected by this lock } finally { l.unlock(); }Copy the code
Lock interface implementation Class: Class ReentrantLock repeatable Lock
class X {
private final ReentrantLock lock = new ReentrantLock();
// ... public void m() {
lock.lock();
// block until condition holds
try {
// ... method body
} finally {
lock.unlock()
}
Copy the code
Lock Narrows the resource scope of the Lock.
The start () thread does not start immediately, but just enters the ready state. And so on operating system scheduling.
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; Class Ticket {// Private int number = 300; private Lock lock= new ReentrantLock(); public void saleTicket(){ lock.lock(); Try {if(number>0){system.out.println (thread.currentThread ().getName())+" +"; } } finally { lock.unlock(); }}} /* With high cohesion and low coupling, Thread Operation Resource class */ Public Class SaleTicket {public static void main(String[] args) {Ticket Ticket = new Ticket(); new Thread(new Runnable (){ @Override public void run() { for(int i=1; i<=300; i++){ ticket.saleTicket(); } } }, "t1").start(); new Thread(new Runnable (){ @Override public void run() { for(int i=1; i<=300; i++){ ticket.saleTicket(); } } }, "t2").start(); new Thread(new Runnable (){ @Override public void run() { for(int i=1; i<=300; i++){ ticket.saleTicket(); } } }, "t3").start(); }}Copy the code
Thread states: NEW (created), RUNNABLE (ready to run),BLOCKED (BLOCKED),
WAITING(silly WAITING),TIMED_WAITING
TERMINATED(to be TERMINATED, meaning execution ends or the thread is dead)
wait/sleep
The current thread is suspended. What’s the difference?
Let go of the lock in my hand
I still have the lock in my hand
Lamda expression:
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; Class Ticket {// Private int number = 300; private Lock lock= new ReentrantLock(); public void saleTicket(){ lock.lock(); Try {if(number>0){system.out.println (thread.currentThread ().getName())+" +"; } } finally { lock.unlock(); }}} /* With high cohesion and low coupling, Thread Operation Resource class */ Public Class SaleTicket {public static void main(String[] args) {Ticket Ticket = new Ticket(); new Thread(()->{ for(int i=1; i<=300; i++){ ticket.saleTicket(); } }, "t1").start(); new Thread(()->{ for(int i=1; i<=300; i++){ ticket.saleTicket(); } }, "t2").start(); new Thread(()->{ for(int i=1; i<=300; i++){ ticket.saleTicket(); } }, "t3").start(); }}Copy the code
Functional interfaces, only one method, java8 after the interface can have implementation method (default).
02. Producer and consumer review
Judge/work/inform
Case production: the merchant produces a cake and the user consumes one.
class Cake{ private int number =0; Public synchronized void increment() throws InterruptedException {// Determine if(number! =0){ this.wait(); } // otherwise generate cake number++; System.out.println(Thread.currentThread().getName()+"\t"+number); // notifyAll(); // notifyAll(); } public synchronized void decrement() throws InterruptedException {// Check if(number==0){this.wait(); } // consume number--; System.out.println(Thread.currentThread().getName()+"\t"+number); // Notification, which can be prepared to consume this.notifyall (); } } public class Foods { public static void main(String[] args) { Cake cake = new Cake(); new Thread(()->{ for (int i=1; i<=10; i++){ try { cake.increment(); } catch (InterruptedException e) { e.printStackTrace(); }}}," make a cake ").start(); new Thread(()->{ for (int i=1; i<=10; i++){ try { cake.decrement(); } catch (InterruptedException e) { e.printStackTrace(); }}}," buy cake ").start(); }}Copy the code
If you have multiple chefs and multiple consumers.
In multithreaded interactions, false awakenings by multiple threads must be prevented
You must use “while” instead of “if”, otherwise you will make an error
.
class Cake{ private int number =0; Public synchronized void increment() throws InterruptedException { =0){ this.wait(); } // otherwise generate cake number++; System.out.println(Thread.currentThread().getName()+"\t"+number); // notifyAll(); // notifyAll(); } public synchronized void decrement() throws InterruptedException {// Judge while (number==0){this.wait(); } // consume number--; System.out.println(Thread.currentThread().getName()+"\t"+number); // Notification, which can be prepared to consume this.notifyall (); } } public class Foods { public static void main(String[] args) { Cake cake = new Cake(); new Thread(()->{ for (int i=1; i<=10; i++){ try { cake.increment(); } catch (InterruptedException e) { e.printStackTrace(); }}},"A1 make cake ").start(); new Thread(()->{ for (int i=1; i<=10; i++){ try { cake.decrement(); } catch (InterruptedException e) { e.printStackTrace(); }}},"B1 buy cake ").start(); new Thread(()->{ for (int i=1; i<=10; i++){ try { cake.increment(); } catch (InterruptedException e) { e.printStackTrace(); }}},"A2 make cake ").start(); new Thread(()->{ for (int i=1; i<=10; i++){ try { cake.decrement(); } catch (InterruptedException e) { e.printStackTrace(); }}},"B2 buy cake ").start(); }}Copy the code
03. New version of producer and consumer writing (using Lock,Condition)
public interface
ConditionCondition factors the Object monitor methods (wait, notify, and notifyAll) into different objects to get each Object with multiple wait sets by locking them together with any combination of effects. Lock replaces the use of synchronized methods and statements, and Condition replaces the use of object monitor methods.
The official way is very official:
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(); } } public Object take() throws InterruptedException { lock.lock(); try { while (count == 0) notEmpty.await(); Object x = items[takeptr]; if (++takeptr == items.length) takeptr = 0; --count; notFull.signal(); return x; } finally { lock.unlock(); }}}Copy the code
After the improvement:
package Part1; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; class Food{ private int number = 0; private Lock lock = new ReentrantLock(); private Condition condition1 = lock.newCondition(); Private Condition condition2 = condition.newcondition (); // Consumer public void increment() throws InterruptedException {lock.lock(); try{ while (number! =0){ condition1.await(); } number++; System.out.println(thread.currentThread ().getName()+" production \t"+number); condition2.signal(); // Wake up the consumer}finally {lock.unlock(); Public void decrement() throws InterruptedException {lock.lock(); try{ while (number==0){ condition2.await(); } number--; System. The out. Println (Thread. CurrentThread (). The getName () + "\ t" + number); condition1.signal(); }finally { lock.unlock(); } } } public class Produce_customer { public static void main(String[] args) { Food food = new Food(); new Thread(()->{ for(int i=1; i<=30; i++){ try { food.increment(); } catch (InterruptedException e) { e.printStackTrace(); } } },"t1").start(); new Thread(()->{ for(int i=1; i<=30; i++){ try { food.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } },"t2").start(); }}Copy the code
Advantages of the new writing method
Accurate notification, accurate wake-up.
Example: Sequential calls between multiple threads
Thread A prints 5 times, thread B prints 10 times, and thread C prints 15 times.
package Part1; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; class First{ private int state = 1; Private Lock Lock = new ReentrantLock(); private Condition condition1 = lock.newCondition(); private Condition condition2 = lock.newCondition(); private Condition condition3 = lock.newCondition(); public void print(int num) { int temp = 1; if(num==5){ temp = 1; }else if(num==10){ temp = 2; }else{ temp = 3; } lock.lock(); // try {while(state! =temp) { if (num == 5) { condition1.await(); } else if (num == 10) { condition2.await(); } else { condition3.await(); For (int I =0; i<num; I++){system.out.println (thread.currentthread ().getname ()+" work!" ); } if(num==5){ state = 2; condition2.signal(); }else if(num==10){ state = 3; condition3.signal(); }else{ state = 1; condition1.signal(); } } catch (InterruptedException e) { e.printStackTrace(); }finally { lock.unlock(); } } } public class Multithreading { public static void main(String[] args) { First first = new First(); new Thread(()->{ for(int i=0; i<5; i++){ first.print(5); } },"A").start(); new Thread(()->{ for(int i=0; i<5; i++){ first.print(10); } },"B").start(); new Thread(()->{ for(int i=0; i<5; i++){ first.print(15); } },"C").start(); }}Copy the code
Lock 04.
Synchronized locks the entire resource class, even though it is in methods.
If there are multiple synchronized methods in an object, any thread that calls one of the synchronized methods at any given time must wait. In other words, only one thread can access the synchronized methods at any given time
The lock is on the current object this, and after this is locked, no other thread can access other synchronized methods on the current object
If you add a normal method, it doesn’t have anything to do with a synchronous lock. If you change it to two objects, it doesn’t have the same lock.
All non-statically synchronized methods use the same lock — the instance object itself, the basis of synchronized synchronization: every object in Java can be used as a lock.
For normal synchronization methods, the lock is the current instance object. For statically synchronized methods, the lock is the class object of the current class. For synchronized method blocks, the lock is an object configured in Synchonized parentheses.
When a thread attempts to access a synchronized block of code, it must first acquire the lock and release it when it exits or throws an exception.
That is, if a non-statically synchronized method of an instance acquires a lock, the other non-statically synchronized methods of the instance must wait for the method that acquires the lock to release the lock. However, the non-statically synchronized methods of other instances use a different lock than the non-statically synchronized methods of the instance. So non-statically synchronized methods that must wait for the instance object to acquire the lock release the lock before they can acquire their own lock.
All statically synchronized methods use the same lock on the —- Class object itself. The two locks (this/Class) are two different objects, so there is no race condition between statically synchronized methods and non-statically synchronized methods.
But once a statically synchronized method acquires the lock, all other statically synchronized methods must wait for that method to release the lock before acquiring it, whether between statically synchronized methods of the same instance object or between statically synchronized methods of different instance objects, as long as they are of the same class!
The list thread is not safe to handle
package Part2; import java.util.ArrayList; import java.util.List; import java.util.UUID; public class NotSafeList { public static void main(String[] args) { List<String> list = new ArrayList<>(); for(int i=1; i<=3; I++) {new Thread (() - > {list. Add (UUID. RandomUUID (), toString (). The substring (0, 8)); System.out.println(list); },String.valueOf(i)).start(); }}}Copy the code
The results of each execution are inconsistent. There are problems.
If the number of threads increases: directly report the following exception
Concurrent modification is abnormal.
Solutions:
Method 1: Use a Vector collection, which is thread-safe with a synchronization lock. But access performance degrades.
package Part2; import java.util.ArrayList; import java.util.List; import java.util.UUID; import java.util.Vector; public class NotSafeList { public static void main(String[] args) { List<String> list = new Vector<>(); for(int i=1; i<=30; I++) {new Thread (() - > {list. Add (UUID. RandomUUID (), toString (). The substring (0, 8)); System.out.println(list); },String.valueOf(i)).start(); }}}Copy the code
Method 2: Use the collection utility class although it is more efficient than ventor. Data consistency decreases.
package Part2; import java.util.*; public class NotSafeList { public static void main(String[] args) { List<String> list = Collections.synchronizedList(new ArrayList<>()); for(int i=1; i<=30; I++) {new Thread (() - > {list. Add (UUID. RandomUUID (), toString (). The substring (0, 8)); System.out.println(list); },String.valueOf(i)).start(); }}}Copy the code
Class CopyOnWriteArrayList
(key)
package Part2; import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; public class NotSafeList { public static void main(String[] args) { List<String> list =new CopyOnWriteArrayList<>(); for(int i=1; i<=30; I++) {new Thread (() - > {list. Add (UUID. RandomUUID (), toString (). The substring (0, 8)); System.out.println(list); },String.valueOf(i)).start(); }}}Copy the code
Read/write separation:
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(); }}Copy the code
Make a copy for writing, and the original copy for reading. After writing, the latest version replaces the original version.
When writing copy
A Copyonwrite container is a container for copying while writing. When adding an element to a container, instead of adding object[] directly to the current container, icopy first adds mobject[] to the current container. SetArray (newELements); setArray(newELements); setArray(newELements); . The advantage of this is that concurrent reads can be made to the Copyonwrite container without locking, since no elements are being added to the current container. So the Copyonwrite container is also an idea of read-write separation, reading and writing different containers
06. The set thread is not handled safely
Solution one: Use the Collections collection class
package Part2; import java.util.Collections; import java.util.HashSet; import java.util.Set; import java.util.UUID; public class NotSafeSet { public static void main(String[] args) { Set<String> set = Collections.synchronizedSet(new HashSet<>()); for(int i=1; i<=30; I++) {new Thread (() - > {set. The add (UUID. RandomUUID (), toString (). The substring (0, 8)); System.out.println(set); },String.valueOf(i)).start(); }}}Copy the code
Method two: Use the JUC class java.util.concurrent
Class CopyOnWriteArraySet<E>
Copy the code
Example:
package Part2; import java.util.*; import java.util.concurrent.CopyOnWriteArraySet; public class NotSafeSet { public static void main(String[] args) { Set<String> set = new CopyOnWriteArraySet<>(new HashSet<>()); for(int i=1; i<=30; I++) {new Thread (() - > {set. The add (UUID. RandomUUID (), toString (). The substring (0, 8)); System.out.println(set); },String.valueOf(i)).start(); }}}Copy the code
Underneath a HashSet is a HashMap
But Set is a value, and HashMap is a key-value pair. The underlying HashSet calls the PUT method of the HashMap.
The value is stored on the key, which is an object PRESENT constant.
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
private static final Object PRESENT = new Object();
Copy the code
The underlying elements of a HashMap are arrays, linked lists, and red-black trees of Nodes
HashMap() constructs an empty HashMap, Default initialCapacity (16) and default loadFactor (0.75) HashMap(int initialCapacity, float loadFactor) constructs an empty HashMap with the specified initialCapacity and loadFactor.Copy the code
Used in actual development:
HashMap(int initialCapacity, float loadFactor) constructs an empty HashMap with the specified initialCapacity and loadFactorCopy the code
07. The map thread is not processed safely
Not solve safety problems: method 1: use the HashTable method 2: use the Collections of the Collections, synchronizedMap (new HashMap < > ());
Method 3:
java.util.concurrent
Class ConcurrentHashMap<K,V>
Copy the code
ArrrayList has been expanded by half and HashMap has been expanded by twice.
The underlying principles of HashMap
Don’t let dream just be your dream.