Statement: 1 ***
2. Because it is a personal summary, write the article with the most concise words
3. If there is any mistake or improper place, please point out
I Front Foundation:
-
The IO operation does not occupy cpu, but we generally copy files using [blocking IO]. At this time, it is equivalent to that the thread does not use cpu, but needs one
Waiting for the end of IO failed to make full use of threads.
Therefore, there are the following [non blocking IO] and [asynchronous IO] optimization
-
synchronized synchronization is mutually exclusive
-
The essence of a lock is that everyone has mutually exclusive access to a shared resource
-
The stack is thread private and the heap is shared by multiple threads;
However, it does not mean that the local variables in the thread stack are accessed by other threads, and escape analysis needs to be carried out first;
For example, if the local variable is passed to the thread opened inside the method, the variable will escape from the method, and there will be thread safety problems
-
Process is the encapsulation of runtime program and the basic unit of resource scheduling and resource allocation of operating system
Thread is the basic unit of cpu scheduling
The tube side is the monitor, i.e. synchronized lock
For example, a running Java program is a JVM process, in which there are both user threads and GC garbage collection threads (daemon threads); It can be seen that there can be multiple threads in a process
-
Whether it is the main thread or the custom thread, only the surviving JVM will not end;
When there are only daemon threads, the JVM ends
-
The lock granularity should not be too large, but not too small; Both concurrency and security should be considered
Method of thread viewing
Windows
tasklist view process
taskkill kill process
Linux
PS - TF - P < PID > view all threads of a process (PID)
Top - H - P < PID > view all threads of a process (PID)
Java
Jstack < PID > view all thread states of a Java process (PID)
Stack frame:
Each stack consists of multiple stack frames, corresponding to the memory occupied by each method call
Each thread can only have one active stack frame, corresponding to the method currently executing
The stack frame includes: local variable, return address, operand stack, args parameter and lock record
Multithreading is to make full use of cpu resources and will not block the whole business process because of a single block.
Advantages of multithreaded serial over single thread:
Multi thread serial, even if one thread is blocked, it will not affect other threads;
Moreover, when the critical area is not modified and the race condition occurs, there is no need to lock, and the advantage is more obvious
Synchronization / asynchrony is viewed from the business logic relationship between multiple threads or callback notification mechanism
Serial / parallel is from the perspective of the actual execution process between multiple threads
Asynchronous business: Commodity detail business & comment business
Synchronization business: login function business & shopping cart function business
Generally speaking, because the business is asynchronous, it leads to parallel execution; Because the service is synchronous, it leads to serial execution
Synchronization:
- Two things maintain some relative relationship
- syncronized mutex
- There is no callback on the message notification mechanism
- Business synchronization, with execution sequence
join(): queue jumping, that is, waiting for this thread to end
join(3000L) can jump the queue for 3s at most. When it times out, main won't wait for t1. If t1 goes, main continues to go down
// t1 takes 1s, t2 takes 2s, and the total cost is not 3s, but 2s; t1 and t2 can be regarded as running in parallel public static main(String[] args){ Thread t1 = new Thread(( ) -> { sleep(1); r1 = 10; }); Thread t2 = new Thread(( ) -> { sleep(2); r2 = 20; }); t1.start( ); t2.start( ); t1.join( ); //It is called by the main thread, so jump the queue before main t2.join( ); }
yield(): give way
LockSupport.park( ) LockSupport.unpark( );
You can call unpark first, and the next park will be invalid; Note that the unpark called first can only be accumulated once and cannot be superimposed
Thread status at the operating system level:
Thread state at JVM level:
The RUNNABLE state at the Java level covers the [RUNNABLE state], [running state] and [blocking state] at the operating system level (thread blocking caused by BIO can not be distinguished in Java and is still considered RUNNABLE)
The blocking states at the Java level include: blocked, waiting and timed_ Waiting (wait with time)
Several ways of thread communication:
- Shared memory (global variable)
- synchronized lock
- wait( )/notify( )
- join( )
- Concurrent tools: CountDownLatch, CyclicBarrier
- The Conduit
- Semaphore
II synchronized bottom layer:
The bottom layer of synchronized is Monitor, which is translated into Monitor, heavyweight lock or tube pass
Monitor infrastructure:
- Owner the thread that snatched the lock
- EntryList thread blocked due to lock failure
- The thread of WaitSet wait is equivalent to a lounge; So wait must be executed in synchronized
synchronized bytecode:
The astore in line 4 is the reference to store the lock, because the lock needs to be found and released later
There are two monitorexits after the monitorenter. The first monitorexit is to release the lock during normal execution, and the second monitorexit is to release the lock when an exception occurs
The lock related information (thread id, whether it is a biased lock, and the number of lock reentry) is stored in MarkWord in the object header
JDK6 introduces the bias lock & lightweight lock, and has the concept of lock upgrade
1. Deflection lock:
When there is no competition (just one thread), the lightweight lock still needs to perform CAS operation every time it re enters
Bias lock: only when CAS is used for the first time, the thread ID is set to the Mark Word header of the object, and then it is found that the thread ID is its own, which means there is no competition and there is no need to re CAS
Biased locks can be redirected to other threads
Undo deflection lock:
That is, upgrading to a lightweight lock requires STW
If the undo bias reaches a certain threshold, all objects of the entire class become unbiased
2. Lightweight lock:
Multiple threads stagger access, and lightweight locks can be used to optimize when there is no competition
3. Heavyweight lock:
There is competition between multiple threads
The state of the synchronized lock can only be upgraded and cannot be degraded
Other lock concepts:
Spin lock: don't rush to block after the lock grabbing failure, but try several times in multiple cycles; Because there is a price for threads to switch between blocking state and runnable state
It is implemented using CAS
Lock inflation: that is, lock upgrade, bias lock - > lightweight lock - > heavyweight lock
Lock coarsening: Several synchronized lock objects are coarsened into a synchronized code block without other code in the middle
Lock elimination: after escape analysis, if it is found that the local variable does not escape from this method, the lock added to this local variable will be eliminated
Deadlock: two threads lock each other's lock, and are unwilling to release their own lock first, resulting in a deadlock of waiting for each other
Generally, a synchronized code block is nested in a synchronized code block
Conditions for deadlock occurrence:
1. Hold each other's locks, And they are unwilling to release the existing lock first 2. Lock for each other, Non obtainable 3. No external interference interrupt
Detailed version:
- Mutually exclusive condition: the process is exclusive to the allocated resources, that is, a resource can only be occupied by one process until it is released by the process
- Request and hold condition: when a process is blocked due to the resources occupied by the request, it will hold the obtained resources
- No deprivation condition: no other process can deprive and occupy any resource before it is released by the process
- Loop waiting condition: when a deadlock occurs, the waiting process must form a loop (similar to an endless loop), causing permanent blocking.
Solution:
1. Active abandonment of lock: use if+tryLock, It is not mandatory to acquire a lock, If you can get it, you can get it back true Then execute,Can't get back false Even if it's done, it won't be implemented 2. Passive abandonment lock: Interruption of external intervention, call interrupt method
Livelock: two threads perform opposite operations. For example, thread A performs + + operation on cnt with an initial value of 10, and thread B performs - operation on cnt;
The two threads have been running, but the value of cnt can't reach 0 for a long time and can't finish running
Fair lock: all threads are covered with rain and dew to prevent some threads from being too strong and finishing all work, resulting in other threads starving and not working;
However, this will reduce concurrency, so it is not recommended;
new ReentrantLock(true); It is a fair lock, and the default is false
Reentrant lock: obtain the same lock repeatedly inside the lock code block, also known as recursive lock
III Multithreading security issues:
Multithreading safety problem: multiple threads write to a shared memory, that is, a race condition occurs in the critical area
If there are multithreaded read-write operations on shared resources in a code block, this code block is called a critical area
When multiple threads execute in the critical area, the results cannot be predicted due to different execution sequences of codes, which is called race condition
False wake-up: when the thread in if is interrupted by wait and wakes up again, the data may have changed, but the judgment is not repeated
1. Atomicity:
-
Atomicity of i + +: at first CNT = 0, thread A + + gets 1, but the pending write is blocked, and B writes to 0-cheng-1. At this time, A continues to write to 1; Caused 0 + + -- = 1
-
Atomicity among multiple operations: thread switching leads to inaccurate if judgment. For example, if (10000 > 8000), if (10000 > 4000) and if (10000 > 2000) go shopping for 10000 yuan. Because the thread is interrupted, there is no time to deduct, and finally 10000-8000-4000-2000 < 0 debt
Solution:
-
Blocking solution: synchronized, AQS
-
Non blocking solution: atomic class (i.e. CAS+volatile)
2. Visibility:
JIT compilation caches the hot code into the working memory, so that the latest data in the main memory is not stored in the working memory
Solution:
- volatile to ensure visibility and order
- With synchronized, JIT compilation is prohibited, and this part of the code is cached in the working memory, no matter who locks it
- Disable JIT globally (not recommended)
3. Order:
cpu adjusts instruction order
For example, load a and b for a while, and cycle 1000 times; When cpu finds that a single thread does not affect the result, it must first load a 1000 times and then load b 1000 times
int a = 0; boolean flag = false; public void writer( ){ a = 1; flag = true; } public void reader( ){ if(flag){ int i = a + a; } }
Solution:
Locked, synchronized or AQS
To sum up:
-
volatile can solve the problem of visibility & order
-
Optimistic lock CAS can solve the problem of atomicity, visibility and order
-
Pessimistic lock synchronized or AQS can solve the problem of atomicity & visibility & ordering
-
Using private and final modification methods can avoid some problems, because otherwise, in subclasses, creating new threads and allowing new threads to share list local variables will be a disaster
-
The as if serial rule ensures that the instruction rearrangement in a single thread will not affect the execution result
The happens before rule ensures that the instruction rearrangement under multithreading will not affect the execution result
Happens before rule:
It specifies which write operations are visible to the read operations of other threads. It is a summary of a set of rules for visibility and ordering
One operation happens before another operation, indicating that the result of the first operation is visible to the second operation
- The writing of the variable before the thread unlocks m is visible to the reading of the variable by other threads that lock m next
- The writing of volatile variable by thread is visible to the reading of this variable by other threads
- The writing of the variable before the thread starts is visible to the reading of the variable after the thread starts
- The writing of variables before the end of a thread is visible to the reading of other threads after they know that it ends (for example, other threads call t1.isAlive() or T1 Join() wait for it to end)
- Thread t1 writes to the variable before interrupting t2(interrupt), which is visible to other threads after they know that T2 is interrupted (through t2.interrupted or t2.isInterrupted)
- The writing of the default value of the variable (0, false, null) is visible to the reading of the variable by other threads
- Transitivity, if X - > y and Y - > Z, then there is X - > Z
Underlying principle of volatile:
It's a lighter lock
Visibility: when a variable modified by volatile performs a write operation: immediately brush the data of this variable in the working memory to the main memory, and invalidate the cache of this variable in the working memory of other threads
Orderliness: shared variables modified by volatile will join the memory barrier when they are read and written, preventing other read and write operations from crossing the barrier
volatile read-write barriers can only prevent instruction rearrangement in the same thread
The sorting rules of volatile in JMM memory model are:
-
When the first operation is volatile read, no matter what the second operation is, it cannot be reordered
-
When the second operation is volatile write, no matter what the first operation is, it cannot be reordered
-
When the first operation is volatile write and the second operation is volatile read, reordering cannot be performed
JMM(Java memory model)
JMM defines the visibility and order of data;
framework:
- Working memory thread private
- The main memory thread is public
Memory barrier:
Load is read and Store is write
-
LoadLoad
Load1, LoadLoad, Load2; That is, first load1, then load2
-
StoreStore
Store1, StoreStore, Store2; That is, first store1, then store2
-
LoadStore
Load1, LoadLoad, Store2; That is, load1 first, then store2
-
StoreLoad
Store1, StoreStore, Load2; That is, first store1, then load2
Transfer case:
public class ExerciseTransfer { public static void main(String[] args) throws InterruptedException { Account a = new Account(1000); Account b = new Account(1000); Thread t1 = new Thread(( ) -> { for (int i = 0; i < 1000; i++) { a.transfer(b, randomAmount( )); } }, "t1"); Thread t2 = new Thread(( ) -> { for (int i = 0; i < 1000; i++) { b.transfer(a, randomAmount( )); } }, "t2"); t1.start( ); t2.start( ); t1.join( ); t2.join( ); log.debug("total:{}", (a.getMoney( ) + b.getMoney( ))); } static Random random = new Random( ); public static int randomAmount( ) { return random.nextInt(100) + 1; } } class Account { private int money; public Account(int money) { this.money = money; } public int getMoney( ) { return money; } public void setMoney(int money) { this.money = money; } public void transfer(Account target, int amount) { // Note that this cannot be locked because a thread a.transfer(b, randomAmount());, Another thread b.transfer(a, randomAmount()); // When this is a, b of thread 2 ignores this(a) lock at all synchronized (Account.class) { if (this.money > amount) { this.setMoney(this.getMoney( ) - amount); target.setMoney(target.getMoney( ) + amount); } } } }
IV CAS and atomic class:
CAS:
CAS(compare and set/swap) is an optimistic lock. Its essence is not a lock, but a while loop to judge the version number
Pessimistic lock VS optimistic lock:
Pessimistic locks are implemented by blocking, while optimistic locks use loops instead of blocking
Pessimistic lock: applicable to the scene of fierce lock competition (less reading and more writing)
Advantages: the thread can't grab the lock, and the cpu will be released when it is blocked
Disadvantages: thread switching state has certain overhead
Optimistic lock: it is applicable to the scenario where the lock competition is not fierce (more reading and less writing) and there are more cpu cores
Advantages: threads do not need to switch states
Disadvantages: the thread always executes while, occupying cpu
- CAS is based on the idea of optimistic locking: I'm not afraid of other threads to modify shared variables. Even if I do, it doesn't matter. I'll try again at a loss
- Synchronized is based on the idea of pessimistic lock: prevent other threads from modifying shared variables. When I lock, you don't want to change it. After I change it and know how to unlock it, you will have a chance
Atomic class:
The bottom layer uses CAS+volatile to realize lock free concurrency
Four atomic classes in the JUC package:
-
Basic type
Update base type
- AtomicInteger: shaping atomic classes
- AtomicLong: long integer atomic class
- AtomicBoolean: Boolean atomic class
-
Array type
Update an element in the array
- AtomicIntegerArray: shaping array atomic class
- AtomicLongArray: long integer array atomic class
- AtomicReferenceArray: reference type array atomic class
-
reference type
- AtomicReference
- AtomicStampedReference:
- AtomicMarkableReference:
-
Update the fields of the class
It needs to be used with volatile, which modifies this field
- AtomicIntegerFieldUpdater: updater for integer fields
- AtomicLongFieldUpdater: updater for long integer fields.
- AtomicReferenceFieldUpdater: updater of reference type
ABA question:
The value a becomes B and then back to A. do these two a think it is the same version?
AtomicReference and AtomicMarkableReference think so (that is, there is an ABA problem), and AtomicStampedReference thinks not
Solution: use version number
LongAdder:
The accumulation efficiency of LongAdder is higher than AtomicLong:
LongAdder is divided into base and cells:
- When there is no concurrent competition or the cell array is being initialized, CAS will be used to accumulate to the base
- When there is concurrent competition, the cells array will be initialized. The number of cells in the array can be modified in parallel by how many threads are allowed; Finally, add up each cell in the array and add base to get the final value
It is equivalent to reducing the granularity of the lock, locking only the cells accumulation unit, rather than locking all the cells
UnSafe:
This class is very low-level and we are not recommended to use it directly
The direct memory of CAS and NIO is realized by calling UnSafe at the bottom
V Common thread safety classes:
String
Integer
StringBuffer
Random
Vector
Hashtable
java. util. Classes under concurrent package
Safety here means that multiple threads operating on the same instance will not cause thread safety problems
String and Integer are immutable classes. The so-called modification is just the return of a new object
For Hashtable, a single API is atomic, but the combination cannot guarantee its atomicity,
As shown in the following figure: the original idea was to put the value only when table was null. As a result, because the API combination is not atomic, it was put twice
Vi ReentrantLock:
characteristic:
-
Multiple Condition variables are supported, that is, there are multiple WaitSet lounges, and you can only wake up the threads of one lounges
await signal signalall
-
You can use tryLock
-
Manual release lock
-
Reentrant
-
You can set the timeout
-
Can be set to fair lock
-
Interruptible
ReentrantReadWriteLock:
A read lock is a shared lock, a write lock is an exclusive lock, and a write lock can be downgraded to a read lock
VII Callable, Future:
Callable:
// There is a return value; If an exception is thrown, the subclass does not have to try catch class MyThread implements Callable<Integer> { @Override public Integer call( ) throws Exception { return 200; } }
Future:
-
When you need to perform time-consuming operations in the main thread but do not want to block the main thread, you can hand over these jobs to Future to complete in the background,
When the main thread needs it in the Future, the result can be obtained through the get method of Future
-
Treat Future as an object to save the results. It does not save the results temporarily, but will save them in the Future (that is, when Callable returns). In order to obtain the results, Callable is required
-
Class relationship: class futuretask < V > implements runnablefuture < V >
interface RunnableFuture<V> extends Runnable, Future<V>
Related API:
FutureTask<Long> futureTask2 = new FutureTask(callable);
- public boolean cancel(boolean flag) / / used to stop tasks
- If not already started, it will stop the task
- If it has been started, the task will be forcibly stopped only when the flag is true
- public Object get() / / used to obtain the result of a task. It is calculated only once
- If the task is completed, it will immediately return results
- Otherwise, it will block, wait for the task to complete, and then return the result; So the general get method is put at the end
- public boolean isDone() / / judge whether the task is completed
CompletableFuture:
It is the implementation class of Future. It is used to arrange the relationship between multiple threads and has the function of asynchronous callback
VIII Fork/Join:
Fork: split a complex task, recursively
Join: merge the results of split tasks
Case: calculate 1 + 2 + 3... + 1000, split it into subtasks, and add 100 numbers to each subtask
class TaskExample extends RecursiveTask<Long> { private int start; private int end; private long sum; public TaskExample(int start, int end) { this.start = start; this.end = end; } @Override protected Long compute( ) { System.out.println("task" + start + "=========" + end + "Accumulation start"); //More than 100 numbers are added and divided, less than direct addition if (end - start <= 100) { for (int i = start; i <= end; i++) { //accumulation sum += i; } } else { //Cut into 2 pieces int middle = start + 100; //Recursive call, split into 2 small tasks TaskExample taskExample1 = new TaskExample(start, middle); TaskExample taskExample2 = new TaskExample(middle + 1, end); //Execution: asynchronous taskExample1.fork( ); taskExample2.fork( ); //Get execution results from synchronization blocking sum = taskExample1.join( ) + taskExample2.join( ); } //Return after adding return sum; } } public class ForkJoinPoolDemo { public static void main(String[] args) { //Define task TaskExample taskExample = new TaskExample(1, 1000); //Object definition execution ForkJoinPool forkJoinPool = new ForkJoinPool( ); //Join task execution ForkJoinTask<Long> result = forkJoinPool.submit(taskExample); //Output results try { System.out.println(result.get( )); } catch (Exception e) { e.printStackTrace( ); } finally { forkJoinPool.shutdown( ); } } }
IX Thread pool:
Why use thread pools:
-
Reuse Thread objects to prevent frequent creation and destruction, so as to reduce resource consumption and improve response speed
-
Improve the manageability of threads and control their upper limit
Seven parameters:
- corePoolSize number of core threads
- maximumPoolSize maximum number of threads
- keepAliveTime the lifetime of the emergency thread
- Unit time unit the survival time unit of the emergency thread
- workQueue wait queue
- threadFactory thread factory can customize the creation of Thread objects, such as setting thread name, whether it is a guard thread, etc
- handler reject policy
- Throw exception AbortPolicy
- Return to the caller to execute CallerRunsPolicy
- Discard this task DiscardPolicy
- Discard the earliest queued task DiscardOldestPolicy
The emergency thread gives priority to new tasks rather than tasks in the waiting queue
Several methods of creating thread pool:
-
Create using Executors factory method
Infinity here means the maximum value of int
- newSingleThreadExecutor: single thread
- newFixedThreadPool: fixed size but infinite waiting queue
- newCachedThreadPool: a thread pool with emergency threads but an infinite number of threads
- newScheduledThreadPool: the number of threads is unlimited and supports scheduled scheduling
-
It is created by passing parameters through the new ThreadPoolExecutor method
Thread destruction:
The emergency thread is automatically destroyed after being idle for a certain time
The core thread can be destroyed by calling shutdown and other methods
-
shutdown
-
Do not receive new tasks
-
The submitted task will be completed
-
Threads calling this method are not blocked
-
The thread pool state changes to SHUTDOWN
-
-
shutdownNow
-
Do not receive new tasks
-
Tasks that have been submitted but are still in the queue and have not started execution will be cancelled
-
Interrupt an executing task with interrupt
-
The thread pool status changes to STOP
-
X Locks commonly used in AQS:
AQS: AbstractQueuedSynchronizer Abstract queue synchronizer
Reentrantlock, futuretask, countdownlatch, cyclicbarrier and semaphore are all implemented based on AQS
AQS can be exclusive or shared
There are some CAS codes inside AQS
Principle of AQS:
AQS maintains a shared resource, and then uses a two-way queue to ensure that threads queue to obtain resources
The volatile state field is used to represent the synchronization state, and the CAS is used for setState
Only the head node in the queue is the holder of the lock, and the tail pointer points to the last waiting thread node in the queue
What are the resource sharing methods of AQS?
This is achieved by modifying the state field
- Exclusive: exclusive. Only one thread can execute, such as ReentrantLock
- Share: share. Multiple threads can execute simultaneously, such as ReentrantReadWriteLock
AQS uses a template approach to design patterns
Custom AQS:
-
Inherit AbstractQueuedSynchronizer
-
Rewrite method:
-
Ishldexclusively(): judge whether the thread is monopolizing resources
-
tryAcquire(int): exclusive mode, trying to obtain resources
-
tryRelease(int): exclusive mode, trying to release resources
-
Tryacquiresered (int): sharing mode, trying to obtain resources; A negative number indicates failure, 0 indicates no available resources, and a positive number indicates available resources
-
Tryrereleaseshared (int): sharing mode, trying to release resources
-
Countdownlatch:
When the value of the counter becomes 0, the thread blocked by the await method will be awakened and continue to execute
Case: after six students leave the classroom one after another, the students on duty can close the door
public class CountDownLatchDemo { public static void main(String[] args) throws Exception { //Define a counter with a value of 6 CountDownLatch countDownLatch = new CountDownLatch(6); //Create 6 students (threads) for (int i = 1; i <= 6; i++) { new Thread(( ) -> { try { if (Thread.currentThread( ).getName( ).equals("Classmate 6")) { Thread.sleep(2000); } System.out.println(Thread.currentThread( ).getName( ) + "I am leaving"); //If the counter is decremented by one, it will not block countDownLatch.countDown( ); } catch (Exception e) { e.printStackTrace( ); } }, "classmate" + i).start( ); } countDownLatch.await( ); // When the counter is 0, it is awakened System.out.println("All left,The current counter is" + countDownLatch.getCount( )); } }
Cyclicbarrier:
Every execution of cyclicbarrier Await() once, the number of obstacles will be increased by one. If the target number of obstacles is reached, it will be executed
Constructor: public cyclicbarrier (int parties, runnable, runnable)
Case: experience 81 difficulties to obtain scriptures
public class CyclicBarrierDemo { private final static int NUMBER = 81; public static void main(String[] args) throws Exception{ //After defining the loop fence and the defined run method (lambda expression), await() will be executed 81 times CyclicBarrier cyclicBarrier = new CyclicBarrier(NUMBER, ( ) -> System.out.println("Gather together" + NUMBER + "Dragon Ball,Now summon the Dragon!!!!!!!!!")); //Define 7 threads to collect dragon beads respectively for (int i = 1; i <= NUMBER; i++) { try { System.out.println("Experience No" + i+"hard"); Thread.sleep(500L) cyclicBarrier.await( ); } catch (Exception e) { e.printStackTrace( ); } } } }
Semaphore:
Define the maximum license. Each semaphore can have at most one license at the same time.
Use the acquire method to obtain the license, and the release method to release the license
Case: 6 cars (threads) grab 3 parking spaces
public class SemaphoreDemo { public static void main(String[] args) throws Exception { //Define 3 parking spaces Semaphore semaphore = new Semaphore(3); //Simulate the parking of 6 cars for (int i = 1; i <= 6; i++) { Thread.sleep(100); //parking new Thread(( ) -> { try { System.out.println(Thread.currentThread( ).getName( ) + "Find a parking space ing"); semaphore.acquire( ); System.out.println(Thread.currentThread( ).getName( ) + "Car parking success!"); Thread.sleep(10000L); //Parking } catch (Exception e) { e.printStackTrace( ); } finally { System.out.println(Thread.currentThread( ).getName( ) + "Slip away, slip away"); semaphore.release( ); } }, "automobile" + i).start( ); } } }
Postmark lock StampedLock:
It is the optimization of read-write lock. It is optimistic. It is similar to CAS, and the stamp is the version number
-
Optimistic reading:
long stamp = lock.tryOptimisticRead( );
//Check stamp
if(!lock.validate(stamp)){
//Lock upgrade
}
-
Read lock:
long stamp = lock.readLock( );
lock.unlockRead(stamp);
-
Write lock:
long stamp = lock.writeLock( );
lock.unlockWrite(stamp);
Xi Big contrast:
wait VS sleep:
- wait must be put in synchronized and used together with Monitor, while sleep does not
- wait is a method in Object, while sleep is a static method in Thread
- wait releases the lock, while sleep does not
wait VS park:
-
Wait and notify must be put in synchronized and used together with Monitor, while Park and unpark do not have to
-
wait is the method in Object, and park is the method in LockSuport
-
notify can only wake up one waiting thread randomly. notifyAll wakes up all waiting threads, which is not so accurate;
Park and unpark are used to block and wake up threads in the unit of threads, which is more accurate
-
Wait & notify cannot notify first, but Park & unpark can unpark first
-
wait releases the lock, but park does not
Interrupt the threads of sleep, wait, join and Park:
- interrupt() / / interrupt the thread
- Interrupt the thread that is in sleep, wait and join, throw InterruptedException and clear the interrupt flag
- If a running or park thread is interrupted, the interrupt flag will be set
- isInterrupted() / / judge whether the thread is interrupted. The interrupt flag will not be cleared
- / / if the thread is interrupted, the interrupt flag () will be cleared
synchronized VS volatile:
-
The bottom layer of synchronized relies on Monitor to ensure atomicity, visibility and order, and the operation is heavy
The bottom layer of volatile is to cache data to working memory by disabling JIT compilation to ensure visibility, and to ensure order by reading and writing memory barrier, but there is nothing to do with atomicity; Light operation
-
synchronized acts on code blocks or methods, and volatile acts on variables
-
synchronized will cause thread blocking, while volatile will not cause thread blocking
synchronized VS Lock:
-
The bottom layer of synchronized is that Monitor is implemented by C + +, while Lock is implemented by Java
-
synchronized will release the Lock automatically, and Lock needs to release the Lock manually (put it in finally)
-
Lock has richer functions, such as:
- tryLock
- Condition (equivalent to multiple waitsets, which can wake up the threads in the specified WaitSet)
- Read / write lock (finer lock granularity and shared read)
-
Performance:
-
When there is no competition, synchronized makes many optimizations, such as biased lock and lightweight lock; Good performance
-
Lock performs better when the competition is fierce (such as read sharing)
-
Runable VS Thread:
- Runable is the interface, Thread is the class, and runable is the parent interface of Thread
- The Thread of Runable needs to be started by the Thread agent, and the Runable implementation class is passed to the Thread constructor as a parameter
- Because of Java's single inheritance and multiple interfaces, Runable is preferred
Runable run( ) VS Callable call( ):
- run has no return value, while call has a return value, so it can be used in combination with Future
- The run method itself does not throw an exception, so the subclass can only try catch, while the call method itself throws an exception, and the subclass does not have to try catch
execute VS submit:
The execute() method is used to submit tasks that do not need a return value, while the submit() method is used to submit tasks that need a return value
Therefore: the execute() method can only execute tasks of Runnable type, while the submit() method can execute tasks of Runnable and Callable types
Deadlock VS livelock:
A deadlock is when two threads wait for each other and are in a blocked state
The livelock is not blocked, it is moving all the time, and it is constantly trying, but the conditions have not been met
XII Code case:
Thread eight lock problem:
-
import java.util.concurrent.TimeUnit; //byPlane first, then byCar. In fact, the two methods are locked at the same time, but there is no time to pause. I can't see it public class Sync_1 { public static void main(String[] args) throws InterruptedException { Vehicle_A vehicle1 = new Vehicle_A( ); new Thread(( ) -> vehicle1.byPlane( )).start( ); TimeUnit.MILLISECONDS.sleep(500L); //Just to eliminate the error that threads start successively, thread 1 is considered to start earlier than thread 2 new Thread(( ) -> vehicle1.byCar( )).start( ); } } class Vehicle_A { public synchronized void byPlane( ) { System.out.println("------byPlane------"); } public synchronized void byCar( ) { System.out.println("------byCar------"); } }
-
import java.util.concurrent.TimeUnit; //After 3s, byPlane and byCar explain that the two methods are locked at the same time public class Sync_2 { public static void main(String[] args) throws InterruptedException { Vehicle_B vehicle1 = new Vehicle_B( ); new Thread(( ) -> vehicle1.byPlane( )).start( ); TimeUnit.MILLISECONDS.sleep(500L); //Just to eliminate the error of thread starting successively new Thread(( ) -> vehicle1.byCar( )).start( ); } } class Vehicle_B { public synchronized void byPlane( ) { try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException ignored) { } System.out.println("------byPlane------"); } public synchronized void byCar( ) { System.out.println("------byCar------"); } }
-
import java.util.concurrent.TimeUnit; //First hello, 3s and then byPlane. Explain that the two methods are not locked at the same time public class Sync_3 { public static void main(String[] args) throws InterruptedException { Vehicle_C vehicle1 = new Vehicle_C( ); new Thread(( ) -> vehicle1.byPlane( )).start( ); TimeUnit.MILLISECONDS.sleep(500L); //Just to eliminate the error of thread starting successively new Thread(( ) -> vehicle1.hello( )).start( ); } } class Vehicle_C { public synchronized void byPlane( ) { try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException ignored) { } System.out.println("------byPlane------"); } public synchronized void byCar( ) { System.out.println("------byCar------"); } public void hello( ) { System.out.println("------hello------"); } }
-
import java.util.concurrent.TimeUnit; //Bycar first, 3S later byPlane, and then explain that the two methods are not locked at the same time public class Sync_4 { public static void main(String[] args) throws InterruptedException { Vehicle_D vehicle1 = new Vehicle_D( ); Vehicle_D vehicle2 = new Vehicle_D( ); new Thread(( ) -> vehicle1.byPlane( )).start( ); TimeUnit.MILLISECONDS.sleep(500L); //Just to eliminate the error of thread starting successively new Thread(( ) -> vehicle2.byCar( )).start( ); } } class Vehicle_D { public synchronized void byPlane( ) { try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException ignored) { } System.out.println("------byPlane------"); } public synchronized void byCar( ) { System.out.println("------byCar------"); } }
-
import java.util.concurrent.TimeUnit; //After 3s, byPlane and byCar explain that the two methods are locked at the same time public class Sync_5 { public static void main(String[] args) throws InterruptedException { Vehicle_E vehicle1 = new Vehicle_E( ); new Thread(( ) -> vehicle1.byPlane( )).start( ); TimeUnit.MILLISECONDS.sleep(500L); //Just to eliminate the error of thread starting successively new Thread(( ) -> vehicle1.byCar( )).start( ); } } class Vehicle_E { public synchronized static void byPlane( ) { try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException ignored) { } System.out.println("------byPlane------"); } public synchronized static void byCar( ) { System.out.println("------byCar------"); } }
-
import java.util.concurrent.TimeUnit; //After 3s, byPlane and byCar explain that the two methods are locked at the same time public class Sync_6 { public static void main(String[] args) throws InterruptedException { Vehicle_F vehicle1 = new Vehicle_F( ); Vehicle_F vehicle2 = new Vehicle_F( ); new Thread(( ) -> vehicle1.byPlane( )).start( ); TimeUnit.MILLISECONDS.sleep(500L); //Just to eliminate the error of thread starting successively new Thread(( ) -> vehicle2.byCar( )).start( ); } } class Vehicle_F { public synchronized static void byPlane( ) { try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException ignored) { } System.out.println("------byPlane------"); } public synchronized static void byCar( ) { System.out.println("------byCar------"); } }
-
import java.util.concurrent.TimeUnit; //Bycar first, 3S later byPlane, and then explain that the two methods are not locked at the same time public class Sync_7 { public static void main(String[] args) throws InterruptedException { Vehicle_G vehicle1 = new Vehicle_G( ); new Thread(( ) -> vehicle1.byPlane( )).start( ); TimeUnit.MILLISECONDS.sleep(500L); //Just to eliminate the error of thread starting successively new Thread(( ) -> vehicle1.byCar( )).start( ); } } class Vehicle_G { public synchronized static void byPlane( ) { try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException ignored) { } System.out.println("------byPlane------"); } public static void byCar( ) { System.out.println("------byCar------"); } }
-
import java.util.concurrent.TimeUnit;//First byCar, 3s and then byplane. Explain that the two methods public class sync are not locked at the same time_ 8 {public static void main (string [] args) throws interruptedexception {vehicle_h vehicle1 = new vehicle_h(); vehicle_h vehicle2 = new vehicle_h(); new thread (() - > vehicle1. Byplane()). Start(); timeunit. Milliseconds. Sleep (500L); / / just to eliminate the error that threads start successively. New thread (() -> vehicle2. byCar( )). start( ); }} class Vehicle_ H { public synchronized static void byPlane( ) { try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException ignored) { } System. out. println("------byPlane------"); } public synchronized void byCar( ) { System.out.println("------byCar------"); }}
Summary:
-
The non static method of lock is lock this, and the static method of lock is the corresponding class of lock class
-
Locking static does not affect non static methods, but affects other static methods
-
Locking non static does not affect static, but affects other non static
Multiple threads alternate printing problem:
-
Three threads T1, T2 and T3 print A, B and C alternately for 10 times, such as abcabcabcabcabc
-
Three threads T1, T2 and T3 print A(5 times), B(10 times) and C(15 times) alternately for 10 times, Such as aaaaaabbbbbbbbbbbbcccccccccccccacaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbcccccccccccccccccaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbcccccccccccccccccccccccccaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbcccccccccccccccccccaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
-
Three threads alternately print 1-100, T1 print 1, T2 print 2, T3 print 3, T1 print 4
-
Two threads print letters and numbers alternately, T1 prints A1, T2 prints B2, T1 prints C3... All the way to Z26, such as A1B2C3
Synchronized & wait & notify solution:
-
public class Demo01 { private static Object lock = new Object( ); private static int num; public static void printABC(String show, int targetNum) throws InterruptedException { for (int i = 0; i < 10; i++) { synchronized (lock) { while (num % 3 != targetNum) { lock.wait( ); } num++; System.out.print(show); lock.notifyAll( ); } } } } class Test01 { public static void main(String[] args) { new Thread(( ) -> { try { Demo01.printABC("A", 0); } catch (InterruptedException e) { e.printStackTrace( ); } }).start( ); new Thread(( ) -> { try { Demo01.printABC("B", 1); } catch (InterruptedException e) { e.printStackTrace( ); } }).start( ); new Thread(( ) -> { try { Demo01.printABC("C", 2); } catch (InterruptedException e) { e.printStackTrace( ); } }).start( ); } }
-
public class Demo02 { private static Object lock = new Object( ); private static int num; public static void printABC(String show, int targetNum) throws InterruptedException { for (int i = 0; i < 10; i++) { synchronized (lock) { while (num % 3 != targetNum) { lock.wait( ); } num++; for (int j = 0; j < 5 * (targetNum + 1); j++) { System.out.print(show); } lock.notifyAll( ); } } } } class Test02 { public static void main(String[] args) { new Thread(( ) -> { try { Demo02.printABC("A", 0); } catch (InterruptedException e) { e.printStackTrace( ); } }).start( ); new Thread(( ) -> { try { Demo02.printABC("B", 1); } catch (InterruptedException e) { e.printStackTrace( ); } }).start( ); new Thread(( ) -> { try { Demo02.printABC("C", 2); } catch (InterruptedException e) { e.printStackTrace( ); } }).start( ); } }
-
public class Demo03 { private static Object lock = new Object( ); private static int num; public static void print_1_100(String threadName, int targetNum) throws InterruptedException { // Print num after num [0,99] num + + while (true) { synchronized (lock) { while (num % 3 != targetNum) { lock.wait( ); } num++; if (num > 100) { // If you do not wake up other threads, other threads will be wait ing and the program will not end lock.notifyAll( ); break; } System.out.println(threadName + ":" + num); lock.notifyAll( ); } } } } class Test03 { public static void main(String[] args) { new Thread(( ) -> { try { Demo03.print_1_100("T1", 0); } catch (InterruptedException e) { e.printStackTrace( ); } }).start( ); new Thread(( ) -> { try { Demo03.print_1_100("T2", 1); } catch (InterruptedException e) { e.printStackTrace( ); } }).start( ); new Thread(( ) -> { try { Demo03.print_1_100("T3", 2); } catch (InterruptedException e) { e.printStackTrace( ); } }).start( ); } }
-
public class Demo04 { public static Object lock = new Object( ); public static int num; public static void print(int order) throws InterruptedException { while (true) { synchronized (lock) { while (num % 2 != order) { lock.wait( ); } num++; if (num > 26) { lock.notifyAll( ); break; } System.out.print((char) (num - 1 + 'A')); System.out.print(num); lock.notifyAll( ); } } } } class Test04 { public static void main(String[] args) { new Thread(( ) -> { try { Demo04.print(0); } catch (InterruptedException e) { e.printStackTrace( ); } }).start( ); new Thread(( ) -> { try { Demo04.print(1); } catch (InterruptedException e) { e.printStackTrace( ); } }).start( ); } }
Solution of lock & condition version:
-
public class Demo01 { private static int num; public static void printABC(String show, int targetNum, Lock lock, Condition cur, Condition next) throws InterruptedException { for (int i = 0; i < 10; i++) { lock.lock( ); try { while (num % 3 != targetNum) { cur.await( ); } num++; System.out.print(show); next.signal( ); } finally { lock.unlock( ); } } } } class Test01 { private static ReentrantLock lock = new ReentrantLock( ); private static Condition condition1 = lock.newCondition( ); private static Condition condition2 = lock.newCondition( ); private static Condition condition3 = lock.newCondition( ); public static void main(String[] args) { new Thread(( ) -> { try { Demo01.printABC("A", 0, lock, condition1, condition2); } catch (InterruptedException e) { e.printStackTrace( ); } }).start( ); new Thread(( ) -> { try { Demo01.printABC("B", 1, lock, condition2, condition3); } catch (InterruptedException e) { e.printStackTrace( ); } }).start( ); new Thread(( ) -> { try { Demo01.printABC("C", 2, lock, condition3, condition1); } catch (InterruptedException e) { e.printStackTrace( ); } }).start( ); } }
-
public class Demo02 { private static int num; public static void printABC(String show, int targetNum, Lock lock, Condition cur, Condition next) throws InterruptedException { for (int i = 0; i < 10; i++) { lock.lock( ); try { while (num % 3 != targetNum) { cur.await( ); } num++; for (int j = 0; j < 5 * (targetNum + 1); j++) { System.out.print(show); } next.signal( ); } finally { lock.unlock( ); } } } } class Test02 { private static ReentrantLock lock = new ReentrantLock( ); private static Condition condition1 = lock.newCondition( ); private static Condition condition2 = lock.newCondition( ); private static Condition condition3 = lock.newCondition( ); public static void main(String[] args) { new Thread(( ) -> { try { Demo02.printABC("A", 0, lock, condition1, condition2); } catch (InterruptedException e) { e.printStackTrace( ); } }).start( ); new Thread(( ) -> { try { Demo02.printABC("B", 1, lock, condition2, condition3); } catch (InterruptedException e) { e.printStackTrace( ); } }).start( ); new Thread(( ) -> { try { Demo02.printABC("C", 2, lock, condition3, condition1); } catch (InterruptedException e) { e.printStackTrace( ); } }).start( ); } }
-
public class Demo03 { private static int num; public static void print_1_100(String threadName, int targetNum, Lock lock, Condition cur, Condition next) throws InterruptedException { // Print num after num [0,99] num + + while (true) { lock.lock( ); try { while (num % 3 != targetNum) { cur.await( ); } num++; if (num > 100) { // If you do not wake up other threads, other threads will be wait ing and the program will not end next.signal( ); break; } System.out.println(threadName + ":" + num); next.signal( ); } finally { lock.unlock( ); } } } } class Test03 { private static ReentrantLock lock = new ReentrantLock( ); private static Condition condition1 = lock.newCondition( ); private static Condition condition2 = lock.newCondition( ); private static Condition condition3 = lock.newCondition( ); public static void main(String[] args) { new Thread(( ) -> { try { Demo03.print_1_100("A", 0, lock, condition1, condition2); } catch (InterruptedException e) { e.printStackTrace( ); } }).start( ); new Thread(( ) -> { try { Demo03.print_1_100("B", 1, lock, condition2, condition3); } catch (InterruptedException e) { e.printStackTrace( ); } }).start( ); new Thread(( ) -> { try { Demo03.print_1_100("C", 2, lock, condition3, condition1); } catch (InterruptedException e) { e.printStackTrace( ); } }).start( ); } }
-
public class Demo04 { public static int num; public static void print(int order, Lock lock, Condition cur, Condition next) throws InterruptedException { while (true) { lock.lock( ); try { while (num % 2 != order) { cur.await( ); } num++; if (num > 26) { next.signal( ); break; } System.out.print((char) (num - 1 + 'A')); System.out.print(num); next.signal( ); } finally { lock.unlock( ); } } } } class Test04 { private static ReentrantLock lock = new ReentrantLock( ); private static Condition condition1 = lock.newCondition( ); private static Condition condition2 = lock.newCondition( ); public static void main(String[] args) { new Thread(( ) -> { try { Demo04.print(0, lock, condition1, condition2); } catch (InterruptedException e) { e.printStackTrace( ); } }).start( ); new Thread(( ) -> { try { Demo04.print(1, lock, condition2, condition1); } catch (InterruptedException e) { e.printStackTrace( ); } }).start( ); } }