<center>第二章</center>
线程安全知识
- 线程安全的代码其核心在于要对状态访问进行管理,比如 共享的、可变的状态。
- 正确的编程方式是首先使代码正确运行,然后再提高代码的速度。
- 什么是线程安全性
- 当多个线程访问某个类时,这个类始终都能编写出正确的行为。
- 无状态对象一定是线程安全的,比如局部变量。
public class StatelessFactorizer implements Servlet{
public void service(ServletRequest
rep,ServletResponse resp){ BigInteger i=extractFromRequest(rep);
BigInteger[] factors=factor(i);
encodeIntoResponse(rep,resp);
} }
<a><center>以上是否是线程安全的?</center></a>
-
原子性
所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何上下文切换到另一个线程(比如数据库事物)public class StatelessFactorizer implements Servlet{ private long conut=0; public void service(ServletRequest rep,ServletResponse resp){ BigInteger i=extractFromRequest(rep); BigInteger[] factors=factor(i); ++count; encodeIntoResponse(rep,resp); } }
<a><center>** 在多线程里面以上会出现什么问题**</center></a>
+ 由于不恰当执行时序而出现不正确的结果称为竞态条件。
1. 基于一种可能失效的观察结果来做出判断或者执行某个计算,这种竞态条件称为先检查后执行。比如(延迟初始化、懒加载、懒加载代理)
<code>
public class Singleton {
private B instance=null;
public B getInstance() {
if (instance == null) {
instance = new B();
}
return single;
}
</code>
-
复合操作(其实就是多个原子操作)
比如修复上面代码出现的问题,将++count 变为一个原子操作(暂时使用线程安全类来解决)。public class StatelessFactorizer implements Servlet{ private AtomicLong conut=new AtomicLong(); public void service(ServletRequest rep,ServletResponse resp){ BigInteger i=extractFromRequest(rep); BigInteger[] factors=factor(i); count.incrementAndGet(); encodeIntoResponse(rep,resp); } }
-
加锁机制
-
内置锁(互斥锁) 每一个对象都可以做实现同步的锁,并且是可重入的。
public class Widget { public synchronized void doSomething() { } } public class LoggingWidget extends Widget { public synchronized void doSomething() { System.out.println(toString() + ": callingdoSomething"); super.doSomething(); } }
-
-
锁保护状态
- 对于可能被多个线程同时访问的可变状态变量,在访问它时都需要持有同一个锁,我们称状态变量是由这个锁保护的。并且建议每个可变的变量都应该由一个锁来保护。