博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java Concurrency In Practice -Chapter 2 Thread Safety
阅读量:5070 次
发布时间:2019-06-12

本文共 7059 字,大约阅读时间需要 23 分钟。

Writing thread-safe code is managing access to state and in particular to shared, mutable state.

Object's state is its data, stored in state variables such as instance or static fields.

Whether an object needs to be thread-safe depends on whether it will be accessed from multiple threads.

   

Synchronization includes

  1. Synchronized keyword
  2. Volatile variables
  3. Explicit locks
  4. Atomic variables  

Ways to avoid inappropriate synchronization with multiple threads access.

  1. Don't share the state variable across threads;
  2. Make the state variable immutable; or
  3. Use synchronization whenever accessing the state variable.  

Good practice on designing thread-safe classes

  1. Good object-oriented techniques - encapsulation(Don't expose the accessibility of the fields of the class too much).
  2. Immutability
  3. Clear specification of invariants  

2.1 What is Thread Safety

A class is thread-safe if it behaves correctly when 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.  

Thread safe classes encapsulate any needed synchronization so that clients need not provide their own.

   

Stateless objects are always thread-safe.

eg. It is only when servlets want to remember things from one request to another that the thread safety requirement becomes an issue.

   

2.2 Atomicity

Classic operation scenario

  1. read-modify-write: To increment a counter, you have to know its previous value and make sure no one else changes or uses that value while you are in mid‐update.
  2. check‐then‐act: you observe something to be true and then take action based on that observation (create X); but in fact the observation could have become invalid between the time you observed it and the time you acted on it (someone else created X in the meantime), causing a problem (unexpected exception, overwritten data, file corruption).

   

2.2.1 Race Conditions

A race condition occurs when the correctness of a computation depends on the relative timing or interleaving of multiple threads by the runtime.

@NotThreadSafepublic class LazyInitRace {    private ExpensiveObject instance = null;       public ExpensiveObject getInstance() {         if (instance == null)                instance = new ExpensiveObject();         return instance;    }}  

Operations A and B are atomic with respect to each other, 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.  

@NotThreadSafepublic class UnsafeCountingFactorizer implements Servlet {    private long count = 0;       public long getCount() {        return count;    }       public void service(ServletRequest req, ServletResponse resp) {        BigInteger i = extractFromRequest(req);        BigInteger[] factors = factor(i);        ++count;        encodeIntoResponse(resp, factors);    }}    

The java.util.concurrent.atomic package contains atomic variable classes for effecting atomic state transitions on

numbers and object references. By replacing the long counter with an AtomicLong, we ensure that all actions that

access the counter state are atomic.  

@ThreadSafepublic class CountingFactorizer implements Servlet {    private final AtomicLong count = new AtomicLong(0);       public long getCount() {        return count.get();    }       public void service(ServletRequest req, ServletResponse resp) {        BigInteger i = extractFromRequest(req);        BigInteger[] factors = factor(i);        count.incrementAndGet();        encodeIntoResponse(resp, factors);        }}

Principle

Where practical, use existing thread-safe objects, like AtomicLong, to manage your class's state. It is simpler 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.

  

2.3 Locking

Dealing with more than one states currency operations within one object.

  

Principle

To preserve state consistency, update related state variables in a single atomic operation.  

2.3.1 Intrinsic Locks

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 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.)  

2.3.2 Reentrancy

Reentrancy means that locks are acquired on a per-thread rather than per-invocation basis.

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.  

2.4 Guarding State with Locks

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.

For every invariant that involves more than one variable, all the variables involved in that invariant must be guarded by the same lock.

   

put-if-absent operation

if (!vector.contains(element))    vector.add(element);

2.5 Liveness and Performance

Refined solution for concurrent operation on more than on state object inside the Class instance

@ThreadSafepublic class CachedFactorizer implements Servlet {    @GuardedBy("this")    private BigInteger lastNumber;    @GuardedBy("this")    private BigInteger[] lastFactors;    @GuardedBy("this")    private long hits;    @GuardedBy("this")        private long cacheHits;       public synchronized long getHits() {        return hits;    }       public synchronized double getCacheHitRatio() {        return (double) cacheHits / (double) hits;    }       public void service(ServletRequest req, ServletResponse resp) {        BigInteger i = extractFromRequest(req);        BigInteger[] factors = null;        synchronized (this) {            ++hits;            if (i.equals(lastNumber)) {                ++cacheHits;                factors = lastFactors.clone();            }        }        if (factors == null) {                factors = factor(i);                synchronized (this) {                    lastNumber = i;                    lastFactors = factors.clone();                }        }        encodeIntoResponse(resp, factors);    }}  

Principle

  1. 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.
  2. Avoid holding locks during lengthy computations or operations at risk of not completing quickly such as network or console I/O.

转载于:https://www.cnblogs.com/haokaibo/p/Thread-Safety.html

你可能感兴趣的文章
nginx 环境搭建(基于linux)
查看>>
go get 无反应、访问github.com速度慢、没反应问题的解决方案
查看>>
fastJson java后台转换json格式数据
查看>>
php实现定时计划任务
查看>>
架构升级中并发容器的使用的一些方法
查看>>
windows下如何批量修改文件名
查看>>
不同版本系统执行不同注册表的脚本
查看>>
计算智能 Computational Intelligence,CI
查看>>
EXCEL表导入SQL,出现错误 0xc02020c5 的问题解决
查看>>
【六】jquery之HTML代码/文本/值[下拉列表框、多选框、单选框的选中]
查看>>
Rsyslog日志服务器搭建
查看>>
有一个长度为100的数组,请以优雅的方式求出该数组的前10个元素之和
查看>>
Web设计和开发人员非常有用的在线工具
查看>>
盒模型学习(一)
查看>>
javascript ajax获得服务响应头的时间
查看>>
mysql主从集群配置
查看>>
打造自己的程序员品牌(摘自Infoq)
查看>>
压缩请求数据(优化页面加载太慢之一)
查看>>
03-Mysql数据库----安装与管理
查看>>
OpentStack环境搭建
查看>>