读书笔记-----Java并发编程实战(一)线程安全性

线程安全类:在线程安全类中封装了必要的同步机制,客户端无须进一步采取同步措施

示例:一个无状态的Servlet

1 @ThreadSafe
2 public class StatelessFactorizer implements Servlet{
3         public void service(ServletRequest req,ServletResponse resp){
4             BigInteger i = extractFromRequest(req);
5             BigInteger[] factors = factor(i);
6             encodeIntoResponse(resp,factors);
7         }
8 }

这个类是无状态的,因为它既不包含任何域,也不包含任何对其他类中域的引用,计算过程中用到的变量是局部变量没有被共享。

无状态对象一定是线程安全的。

在并发编程中,由于不恰当执行时序而出现不正确结果的情况,称为竞态条件。

最常见的竞态类型就是先检查后执行操作:比如单例中的延迟初始化。

像递增,递减操作看上去只有一个操作,但这个操作并非原子的,会导致结果变得不可靠。这种情况称为:读取-修改-写入的复合操作,也是竞态类型的一种。

对于这些竞态条件类型的操作可以加上同步锁,或者使用一个现有的线程安全类。

如:

 1  @ThreadSafe
 2  public class CountingFactorizer implements Servlet{
 3          private final AtomicLong count = new AtomicLong(0);
 4          public long getCount(){return count.get();}
 5          public void service(ServletRequest req,ServletResponse resp){
 6              BigInteger i = extractFromRequest(req);
 7              BigInteger[] factors = factor(i);
 8              count.incrementAndGet();
 9              encodeIntoResponse(resp,factors);
10          }
11  }

在java.util.concurrent.atomic包中包含了一些原子变量类,用于实现在数值和对象引用上的原子状态转换。

注意:当在不变性条件中涉及多个变量时,各个变量间并不是彼此独立的,而是某个变量的值会对其他变量的值产生约束。因此,当更新某一个变量时,需要在同一个原

子操作中对其他变量同时更新

重入:当某个线程请求一个由其他线程持有的锁时,发出的请求线程会被阻塞。然而内置锁是可以重入的,因此某个线程试图获得一个已经由他自己持有的锁,那么这个请求就会成

功。

这个过程可以描述为:线程请求一个未被持有的锁,JVM记下这个新的锁持有者,计数值置为1,当这个线程再次获取这个锁,计数值将递增。当线程退出同步代码块时,计数器会相

应的递减。当计数值为0时,这个锁将被释放。

示例:

 1 public class Widget{
 2      public synchronized void doSomething(){
 3              ...
 4       }
 5 }
 6 
 7 public class LoggingWidget extends Widget{
 8       public synchronized void doSomething(){
 9                  super.doSomething();
10       }
11 }

使用锁保护时,对于包含多个变量的不变性条件,其中涉及的所有变量都需要由同一个锁来保护。

注意:当执行时间较长的计算或者无法快速完成的操作时,一定不要持有锁。

原文地址:https://www.cnblogs.com/krislight1105/p/3886786.html