Introduce a,
1.1 How to Write thread-safe Code
- The core is to manage state access operations.
- Especially for
Shared (Shared)
andVariable (Mutable)
State access.
1.2 Shared and variable Meanings
- Shared: Means that multiple threads can access it simultaneously
- Mutable: Means that the value of a variable can change over its lifetime.
1.3 Do objects need to be thread-safe?
- Depending on whether it is accessed by multiple threads.
- Here refers to
How objects are accessed in a program
, rather than what the object is supposed to do. - If you want to make objects thread-safe, you need to use synchronization,
Collaborative thread access to the mutable state of an object
.
1.4 How to cooperate with threads to access mutable state of objects?
- The keyword
synchronized
volatile
Type variable- Explicit Lock (explicit lock)
- Atomic variables (JUC package) : Implements atomic state transitions on numeric and object references.
AtomicBoolean
AtomicLong
AtomicInteger
AtomicReference
1.5 How can I fix the problem that multiple threads access the same mutable state variable?
Prerequisite: Assume that this problem is resolved due to the failure to use proper synchronization:
Not shared between threads
The state variable.- Change the state variable to
Immutable variable
。 - Used when accessing state variables
synchronous
.
1.6 How to design a thread-safe class?
- Good object-oriented skills
- unmodifiability
- Explicit invariance specifications
What is thread safety
2.1 define
- When multiple threads access a class, it is always available
Show the right behavior
So let’s call it thisClasses are thread-safe
。 - This class behaves correctly regardless of how the runtime environment is scheduled or how the threads will execute alternately, and without any additional synchronization or coordination in the calling code.
2.2 Definition of correctness
- A class behaves exactly as its specification does.
- In a good specification it is common to define various
The Invarient conditions constrain the state of an object
, and define variousA posterior condition is used to describe the result of an object operation
.
2.3 Stateless objects must be thread-safe.
Atomicity
3.1 Race Condition
- define
- Due to the
Incorrect results resulting from improper execution of timing
。
- Due to the
- Nature:
- Making a judgment or performing a calculation based on an observation that might not work can cause problems.
- Common race conditions
- Check before you perform: Determine the next action based on an observation that may fail.
- Read-modify-write: Defines a transition of an object’s state based on its previous state.
3.2 Compound Operation
- Both of the above race conditions involve compound operations.
- Contains a set of mandatory
Atomic way
Operations performed to ensure thread-safety.
3.3 Atomic Operations
- This operation is an atomic operation for all operations that access the same state, including the operation itself.
Four, locking mechanism
4.1 the built-in lock
- Synchronized code block (
Synchronized Block
Java provides a built-in locking mechanism to support atomicity. - The composition of the synchronous code block: two parts
The object reference to the lock
Block of code protected by a lock
- Syncing block syntax format:
synchronized (lock) {
// Access/modify the lock protected share state
}
Copy the code
static synchronized
Methods toClass object
As a lock.Intrinsic Lock
orMonitor Lock
- Each Java object can be used as a lock to implement synchronization, and these locks are called built-in or monitor locks.
- The thread automatically acquires the lock before entering the synchronized block and releases the lock after exiting the synchronized block.
- Only one thread can execute a block of code protected by the built-in lock at a time.
- Built-in lock is
Reentrant lock
.- Reentrant means
The granularity of the lock acquisition operation is thread
Instead of calling.
- Reentrant means
5. Use locks to protect status
- Pay attention to the point
- Mutable state variables that may be accessed by multiple threads at the same time need to hold the same lock when accessing them. In this case we say that the state variable is protected by the lock.
Sixth, the demo
6.1 Requirements
- Premise: Suppose we want to get the number of requests from the user and increment it.
- requirements: Consider multithreaded execution so that
main
The method outputs the correct result. - The basic class definition is as follows:
public class UserReq {
private long count;
public long getCount(a) {
return count;
}
public void addCount(a) {
// To see the counting effect, loop through the method 1000 times
for (int i = 0; i < 1000; i++) { count++; }}}Copy the code
- Test method: Use
main
Method is executed as follows.
public class UserReqTest {
public static void main(String[] args) throws InterruptedException {
int loopNum = 1000;
UserReq userReq = new UserReq();
for (int i = 0; i < loopNum; i++) {
new Thread(userReq::addCount).start();
}
// The main thread sleeps for a while, waiting for all the child threads to complete.
Thread.sleep(1000 * 5);
System.out.println(userReq.getCount());
// output: 999103. The output here is different every time I do it, but it's not 1000 by 1000}}Copy the code
- instructions:
UserReq
Medium counting methodcount++
Is a compound operation that must be performed atomically to be thread-safe.
6.2 Improvement 1: Use the synchronized keyword
- Improvement methods:
- in
UserReq
Class methodaddCount
To addsynchronized
The keyword. - The lock of the synchronized code block is the object when the method is called
userReq
(created in the main method).
- in
- Improved code: ignore
- Problem: Performance problem
6.2 Improvement: Use atomic variables to count
- Improvement methods:
- in
UserReq
Class uses atomic variablesAtomicLong
Type instead of the underlying data typelong
.
- in
- Improved code:
public class UserReq {
private final AtomicLong count = new AtomicLong(0);
public long getCount(a) {
return count.get();
}
public void addCount(a) {
// To see the counting effect, loop through the method 1000 times
for (int i = 0; i < 1000; i++) { count.incrementAndGet(); }}}Copy the code
// Main output: 1000000
Copy the code
- thinking:
- There’s only one variable that you need to use atomic variables to coordinate state changes, if any
Multiple shared mutable variables
Can thread-safety requirements be met only by adding atomic variables?
Maintaining state consistency requires updating all relevant state variables within a single atomic operation.
- The main thread is not needed
Thread.sleep(1000 * 5)
Wait for the thread to complete; You can useBinary atresia
Reference,【Ch5- Basic Building blocks 】
- There’s only one variable that you need to use atomic variables to coordinate state changes, if any