JCIP-2-Thread Safety

7 篇文章 0 订阅

2-Thread Safety
2.1-What is thread safety
start
Writing thread-safe code is at its core,about managing access to state,and in particular to shared,mutable state
an object’s state is its data,stored in state variables such as instance or static fields
By shared,we mean that a variable could be accessed by multiple threads;by mutable,we mean that its value could change during its lifetime.
Whenever more than one thread accesses a given state variable,and one might write to it,they must coordinate their access to it using synchronization
A program that omits needed synchronization may fail at any moment
3 ways to fix it
Don’t share the state variable across threads
Make the state variable immutable
Use synchronization whenever accessing the state variable
it is far easier to design a class to be thread-safe than to retrofit it for thread-safety later
The less code that has access to a particular variable, the easier it is to ensure that all of it uses the the proper synchronization,and the easier it is to reason about the conditions under which a given variable might be accessed
When designing thread-safe classes,good object-oriented techniques-encapsulation,immutability,and clear specification of invariants -are your best friends
First to make your code right,and then to make it fast
a program that consists entirely of thread-safe classes may not be thread-safe.
a class is thread-safe when it continues to behave correctly when accessed from multiple threads
A class is thread-safe if it behaves correctly accessed from multiple threads,regardless of the scheduling or interleaving of the execution of those threads by the runtime environment,and with no additional synchronization or other coordination on the part of the calling code
No set of operations performed sequentially or concurrently on instances of a thread-safe class can cause an instance to be in an invalid state
2.1.1-Example:a stateless servlet
Stateless:it has no fields and references no fields from other classes
Since the actions of a thread accessing a stateless object cannot affect the correctness of operations in other threads,stateless objects are thread safe
2.2-Atomicity
2.2.1- Race conditions
Instead,it is a shorthand for a sequence of three discrete operations:fetch the current value,add one to it,and write the new value back.
a read-modify-write operation
The possibility of incorrect results in the presence of unlucky timing is so important in concurrent programming that it has a name:a race condition
The most common type of race condition is check-then-act,where a potentially stale observation is used to make a decision on what to do next
It is this invalidation of observation that characterizes most race conditions-using a potentially stale observation to make a decision or perform a computation
2.2.2-Example:race conditions in lazy initialization
The goal of lazy-initialization is to defer initializing an object until it is actually needed while at the same time ensuring that it is initialized only once
Read-modify-write operations
make sure no one else changes or uses that value while your are in mid-update
2.2.3-Compound actions
Operation A and B are atomic with respect to each other if,from the perspective of a thread executing A,when another thread executes B,either all of B has executed or none of it has.An atomic operation is one that is atomic with respect to all operations,including itself,that operate on the same state.
read-modify-write operations must always be atomic.We refer collectively to check-then-act and read-modity-write sequences as compound actions:sequences of operations that must be executed atomically in order to remain thread-safe.
The java.util.concurrent.atomic package contains atomic variable classes for effecting atomic state transitions on numbers and object references
When a single element of state is added to a stateless class,the resulting class will be thread-safe if the state is entirely managed by a thread-safe object
Where practical, use existing thread-safe objects,like AtomicLong,to manage your class’s state.It is simplier to reason about the possible states and state transitions for existing thread-safe objects than it is for arbitrary state variables,and this makes it easier to maintain and verify thread safety.
AtomicReference is a thread-safe holder for an object reference
When multiple variables participate in an invariant,they are not independent:the value of one contains the allowed values of the others.Thus when updating one,you must update the others in the same atomic operation
There is still a window of vulnerability when one has been modified and the other has not
To preserve state consistency,update related state variables in a single atomic operation
2.3-Locking
2.3.1-Intrinsic locks
a build-in locking mechanism:synchronized block
A synchronized block has two parts:a reference to an object that will serve as the lock,and a block of code to be guarded by that lock.A synchronized method is a shorthand for a synchronized block that spans an entire method body,and whose lock is the object on which the method is being invoked.(Static synchronized methods use the Class object for the lock)
Every Java object can implicitly act as a lock for purposes of synchronization; these builtͲin locks are called intrinsic locks
or monitor locks. The lock is automatically acquired by the executing thread before entering a synchronized block and
automatically released when control exits the synchronized block, whether by the normal control path or by throwing
an exception out of the block. The only way to acquire an intrinsic lock is to enter a synchronized block or method
guarded by that lock.
Intrinsic locks in Java act as mutexes (or mutual exclusion locks), which means that at most one thread may own the
lock. When thread A attempts to acquire a lock held by thread B, A must wait, or block, until B releases it. If B never
releases the lock, A waits forever.
2.3.2-Reentrancy
But because intrinsic locks are reentrant, if a thread tries to acquire a lock that it already holds, the request succeeds. Reentrancy means that locks are acquired on a per-thread rather than per-invocation basis. [7] Reentrancy is implemented by associating with each lock an acquisition count and an owning thread. When the count is zero, the lock is considered unheld. When a thread acquires a previously unheld lock, the JVM records the owner and sets the acquisition count to one. If that same thread acquires the lock again, the count is incremented, and when the owning thread exits the synchronized block, the count is decremented. When the count reaches zero, the lock is released.
in which a subclass overrides a synchronized method and then calls the superclass method, would deadlock.
2.4-Guarding state with locks
Holding a lock for the entire duration of a compound action
can make that compound action atomic. However, just wrapping the compound action with a synchronized block is not sufficient;it is needed everywhere that variable is accessed.
For each mutable state variable that may be accessed by more than one thread, all accesses to that variable must be performed with the same lock held. In this case, we say that the variable is guarded by that lock.
the only thing that acquiring a lock prevents any other thread from doing is acquiring that same lock.
Every shared, mutable variable should be guarded by exactly one lock. Make it clear to maintainers which lock that is.
For every invariant that involves more than one variable, all the variables involved in that invariant must be guarded by the same lock.
2.5-Liveness and performance
using two different synchronization mechanisms would be confusing and would offer no performance or safety benefit.
There is frequently a tension between simplicity and performance. When implementing a synchronization policy, resist the temptation to prematurely sacrifice simplicity (potentially compromising safety) for the sake of performance.
Avoid holding locks during lengthy computations or operations at risk of not completing quickly such as network or console I/O.
2.1-What is thread safety
start
Writing thread-safe code is at its core,about managing access to state,and in particular to shared,mutable state
an object’s state is its data,stored in state variables such as instance or static fields
By shared,we mean that a variable could be accessed by multiple threads;by mutable,we mean that its value could change during its lifetime.
Whenever more than one thread accesses a given state variable,and one might write to it,they must coordinate their access to it using synchronization
A program that omits needed synchronization may fail at any moment
3 ways to fix it
Don’t share the state variable across threads
Make the state variable immutable
Use synchronization whenever accessing the state variable
it is far easier to design a class to be thread-safe than to retrofit it for thread-safety later
The less code that has access to a particular variable, the easier it is to ensure that all of it uses the the proper synchronization,and the easier it is to reason about the conditions under which a given variable might be accessed
When designing thread-safe classes,good object-oriented techniques-encapsulation,immutability,and clear specification of invariants -are your best friends
First to make your code right,and then to make it fast
a program that consists entirely of thread-safe classes may not be thread-safe.
a class is thread-safe when it continues to behave correctly when accessed from multiple threads
A class is thread-safe if it behaves correctly accessed from multiple threads,regardless of the scheduling or interleaving of the execution of those threads by the runtime environment,and with no additional synchronization or other coordination on the part of the calling code
No set of operations performed sequentially or concurrently on instances of a thread-safe class can cause an instance to be in an invalid state
2.1.1-Example:a stateless servlet
Stateless:it has no fields and references no fields from other classes
Since the actions of a thread accessing a stateless object cannot affect the correctness of operations in other threads,stateless objects are thread safe
2.2-Atomicity
2.2.1- Race conditions
Instead,it is a shorthand for a sequence of three discrete operations:fetch the current value,add one to it,and write the new value back.
a read-modify-write operation
The possibility of incorrect results in the presence of unlucky timing is so important in concurrent programming that it has a name:a race condition
The most common type of race condition is check-then-act,where a potentially stale observation is used to make a decision on what to do next
It is this invalidation of observation that characterizes most race conditions-using a potentially stale observation to make a decision or perform a computation
2.2.2-Example:race conditions in lazy initialization
The goal of lazy-initialization is to defer initializing an object until it is actually needed while at the same time ensuring that it is initialized only once
Read-modify-write operations
make sure no one else changes or uses that value while your are in mid-update
2.2.3-Compound actions
Operation A and B are atomic with respect to each other if,from the perspective of a thread executing A,when another thread executes B,either all of B has executed or none of it has.An atomic operation is one that is atomic with respect to all operations,including itself,that operate on the same state.
read-modify-write operations must always be atomic.We refer collectively to check-then-act and read-modity-write sequences as compound actions:sequences of operations that must be executed atomically in order to remain thread-safe.
The java.util.concurrent.atomic package contains atomic variable classes for effecting atomic state transitions on numbers and object references
When a single element of state is added to a stateless class,the resulting class will be thread-safe if the state is entirely managed by a thread-safe object
Where practical, use existing thread-safe objects,like AtomicLong,to manage your class’s state.It is simplier to reason about the possible states and state transitions for existing thread-safe objects than it is for arbitrary state variables,and this makes it easier to maintain and verify thread safety.
AtomicReference is a thread-safe holder for an object reference
When multiple variables participate in an invariant,they are not independent:the value of one contains the allowed values of the others.Thus when updating one,you must update the others in the same atomic operation
There is still a window of vulnerability when one has been modified and the other has not
To preserve state consistency,update related state variables in a single atomic operation
2.3-Locking
2.3.1-Intrinsic locks
a build-in locking mechanism:synchronized block
A synchronized block has two parts:a reference to an object that will serve as the lock,and a block of code to be guarded by that lock.A synchronized method is a shorthand for a synchronized block that spans an entire method body,and whose lock is the object on which the method is being invoked.(Static synchronized methods use the Class object for the lock)
Every Java object can implicitly act as a lock for purposes of synchronization; these builtͲin locks are called intrinsic locks
or monitor locks. The lock is automatically acquired by the executing thread before entering a synchronized block and
automatically released when control exits the synchronized block, whether by the normal control path or by throwing
an exception out of the block. The only way to acquire an intrinsic lock is to enter a synchronized block or method
guarded by that lock.
Intrinsic locks in Java act as mutexes (or mutual exclusion locks), which means that at most one thread may own the
lock. When thread A attempts to acquire a lock held by thread B, A must wait, or block, until B releases it. If B never
releases the lock, A waits forever.
2.3.2-Reentrancy
But because intrinsic locks are reentrant, if a thread tries to acquire a lock that it already holds, the request succeeds. Reentrancy means that locks are acquired on a per-thread rather than per-invocation basis. [7] Reentrancy is implemented by associating with each lock an acquisition count and an owning thread. When the count is zero, the lock is considered unheld. When a thread acquires a previously unheld lock, the JVM records the owner and sets the acquisition count to one. If that same thread acquires the lock again, the count is incremented, and when the owning thread exits the synchronized block, the count is decremented. When the count reaches zero, the lock is released.
in which a subclass overrides a synchronized method and then calls the superclass method, would deadlock.
2.4-Guarding state with locks
Holding a lock for the entire duration of a compound action
can make that compound action atomic. However, just wrapping the compound action with a synchronized block is not sufficient;it is needed everywhere that variable is accessed.
For each mutable state variable that may be accessed by more than one thread, all accesses to that variable must be performed with the same lock held. In this case, we say that the variable is guarded by that lock.
the only thing that acquiring a lock prevents any other thread from doing is acquiring that same lock.
Every shared, mutable variable should be guarded by exactly one lock. Make it clear to maintainers which lock that is.
For every invariant that involves more than one variable, all the variables involved in that invariant must be guarded by the same lock.
2.5-Liveness and performance
using two different synchronization mechanisms would be confusing and would offer no performance or safety benefit.
There is frequently a tension between simplicity and performance. When implementing a synchronization policy, resist the temptation to prematurely sacrifice simplicity (potentially compromising safety) for the sake of performance.
Avoid holding locks during lengthy computations or operations at risk of not completing quickly such as network or console I/O.
思维导图

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值