Definition: When multiple threads access a class, this class always shows the correct behavior, so it is thread-safe to call this class long.
Explanation: When multiple threads access a class, regardless of how the runtime environment is scheduled or how those threads will be executed alternately,
And it does not require any additional synchronization or synergy in the keynote code, and this class can show the correct behavior, so it is called thread-safe.
- The necessary synchronization mechanisms are encapsulated in the thread security class, so the client does not need to take further synchronization measures.
Instance: a stateless servlet
@ThreadSafe Public class Implements servlet{ publicvoid service (servletrequest Req,servletresponse resp) { BigInteger I=extractfromrequest (req); biginteger[] factors=factor (i); Encodeintorespose (resp,factors); }}
The servlet extracts the request parameters and then encapsulates them into the servlet's response.
This servlet is the same as most servlets, safethreadtest is stateless, does not contain a package field, and does not contain a reference to another class's domain.
The calculation of local variables in the process only exists on the stack of executing threads, access to safethreadtest does not affect the same access to another thread of the servlet
Results of the calculation. Therefore, stateless objects must be thread-safe.
Instance: a stateful servlet
@NotThreadSafe Public classUnsafethreadtestImplementsservlet{Private LongCount=0; Public LongGetCount () {returncount;} Public voidService (ServletRequest Req,servletresponse resp) {BigInteger i=extractfromrequest (req); biginteger[] Factors=factor (i); Count++; Encodeintorespose (resp,factors); }}
We add a statistic request number and add a state to this servlet, unsafethreadtest is not a thread-safe, although it works correctly in a single thread.
This class is likely to lose some update operations, which looks like an operation, but this operation is missing atomicity . It actually contains three separate operations: Read count, Value +1,
The result is then written to count, which is a "read-to-write" sequence of operations, and the result depends on the previous state.
If in some cases, multiple threads read at the same time to a value of 10, when both are added 1, write 11. The value will be much more biased. in concurrent programming, this occurs due to inappropriate execution timing
The incorrect result is a very important case, it has an official name: competitive conditions (Race codition)
Race condition: When the correctness of a calculation depends on the alternating execution timing of multiple threads, a race condition (i.e. the correct result depends on luck) occurs. The most common type of competition is
"Check after performing (check-then-act) operation", that is, the result of a possible failure of the observation to determine the next action.
Example: Race condition in deferred initialization
@NotThreadSafe Public class lazyinitrace{ private Exprensiveobject instance =null; Public exprensiveobject getinstance () { if(instance==null) Instance=new exprensiveobject (); return instance;} }
If two threads execute at the same time, an instance is generated, so that the instance is completely different. As with most concurrency errors, competitive conditions do not always produce errors, and some inappropriate execution timing is required.
Composite operations: The above two instances with a race condition need to contain a set of operations that need to be performed atomically (or indivisible).
Atomic operations: Operations A and B, if viewed from the thread that executes a, when another thread executes B, either completely executes B, or does not execute B at all, then A and B are atomic to each other.
We use the Atomiclong type variable to count the number of requests that have been processed
@ThreadSafe Public classCountsafethreadtestImplementsservlet{Private FinalAtomiclong count=NewAtomiclong (0); Public LongGetCount () {returncount.get ();} Public voidService (ServletRequest Req,servletresponse resp) {BigInteger i=extractfromrequest (req); biginteger[] Factors=factor (i); Count.incrementandget (); Encodeintorespose (resp,factors); }}
Atomiclong the value to be updated atomically long
. For a description of the atomic variable attribute, see java.util.concurrent.atomic
package specification.
In practice, you should use existing thread-safe classes to manage the state of your classes whenever possible.
Locking mechanism: When a state variable is added to a servlet, the servlet's state can be managed through a thread-safe object to maintain the servlet's thread security, and if you want to add more states to the servlet, do you need only
Is it enough to add more thread-safe state variables?
To maintain state consistency, you need to update all related state variables in a single atomic operation.
Built-in Lock:Java provides a built-in locking mechanism to support atomicity: synchronizing blocks of code.
(1) The method of synchronized is a synchronous block of code that spans the entire method body, where the lock of the synchronous block is the object in which the method is called.
(2) The static synchronized method takes a class object as a lock. (When the class loads, the hotspot virtual machine is generating the Java.lang.Class object in the method area)
Synchronized (lock) {//Access or modify the status of shared protected by lock}
Each Java object can be used as an implementation synchronization lock, which is called a built-in lock or a monitor lock. The thread obtains the lock automatically before it enters the synchronization code block, and the lock is automatically released when the synchronization code block exits.
The only way to get the built-in lock is to enter this lock-protected synchronous code block or synchronization method. Java's built-in lock is equivalent to a mutex, with at most one thread holding the lock. Because there's only one at a time
The thread executes a block of code that has built-in lock protection. Therefore, the synchronous block of code protected by this lock is atomically executed. atomicity in a concurrency environment has the same meaning as atomicity in a transactional application:
A set of statements is executed as an indivisible unit.
Re-entry: When a thread requests a lock held by another thread, the thread that makes the request is blocked. However, because a built-in lock can be reentrant, if a thread tries to get an already owned
Lock, then the request will succeed. "Re-entry" means that the granularity of the operation that acquires the lock is "thread", not a call.
When you access a mutable state variable that may be accessed concurrently by multiple threads, you need to hold the same lock, in which case we call the state variable protected by this lock.
Each shared and mutable variable should be protected by only one lock, so that the maintainer knows which lock it is.
For each invariant condition that contains multiple variables, all of the variables involved require the same lock to protect.
Do not hold locks when performing long-time calculations or operations that cannot be done quickly (for example, network I/O or console I/Os).
What is thread safety?