java线程(四) : 对象的组合

设计线程安全的类:

在设计线程安全类的过程中,需要包含以下三个基本要素:

  • 找出构成对象状态的所有变量
  • 找出约束状态变量的不变性条件
  • 建立对象状态的并发访问策略

如果不了解对象的不变性条件与后验条件,那么就不能确保线程安全性。要满足在状态变量的有效值或状态转换上的各种约束条件,就需要借助于原子性与封装性。

如果在操作中包含有基于状态的先验条件,那么这个操作就称为依赖状态的操作,在并发程序中一直要等到先验条件为真,然后再执行该操作。在java中,等待某个条件为真的各种内置机制(包括等待和通知机制)都与内置加锁机制紧密关联。

实例封闭:

将数据封装在对象内部,可以将数据的访问限制在对象的方法上,从而更容易确保线程在访问数据时总能持有正确的锁。

例: 通过封闭机制来确保线程安全

@ThreadSafe
public class PersonSet {
     @GuardedBy("this")
     private final Set<Person> mySet = new HashSet<Person>();
     public synchronized void addPerson(Person p) {
          mySet.add(p);
     }
     public synchronized boolean containsPerson(Person p) {
          return mySet.contains(p);
     }
}

由于mySet是私有的并且不会溢出,PersonSet的状态完全由它的内置锁保护,因而PersonSet是一个线程安全的类。

封闭机制更易于构造线程安全的类,因为当封闭类的状态时,在分析类的线程安全性时就无须检查整个程序。

通常我们也可以使用一个私有锁来保护状态。

public class PrivateLock{
     private final Object myLock = new Object();
     @GuardedBy("myLock") Widget widget;
     void someMethod() {
          synchronized(myLock) {
               //访问或修改Widget的状态
          }
     }
}     

线程安全性的委托:

如果一个类是由多个独立且线程安全的状态变量组成,并且在所有的操作中都不包含无效状态转换,那么可以将线程安全性委托给底层的状态变量。

例:这里我们实现一个车辆追踪器,可以实时的返回一个车辆当前的位置,也可对某车辆的位置进行修改。

@Immutable
public class Point {
     public final int x, y ;
     public Point(int x, int y) {
          this.x = x;
          this.y = y;
     }
}
@ThreadSafe
public class DelegatingVehicleTracker {
     private final ConcurrentMap<String, Point> locations;
     public DelegatingVehicleTracker(Map<String, Point> points) {
          locations = new ConcurrentHashMap<String, Point>(points);
     }
     public Map<String, Point> getLocations() {
          return Collections.unmodifiableMap (
               new HashMap<String, Point>(locations)
          };
     }
     public Point getLocation(String id) {
          return locations.get(id);
     }
     public void setLocation(String id, int x, int y) {
          if(location.replace(id, new Point(x, y) == null )
               throw new IllegalArgumentException(
                    "invalid vechicle name:  " + id);
     }
}       

如果一个类的多个状态变量之间存在某种联系,如维持着某种不变性条件,那么就不能简单的将线程安全性委托给底层变量。

例:NumberRange类不足以保护它的不变性条件

public class NumberRange {
     //不变性条件:lower <= upper
     private final AtomicInteger lower = new AtomicInteger(0);
     private final AtomicInteger upper = new AtomicInteger(0);
     public void setLower(int i) {
     //注意:不安全的"先检查后执行"
          if( i > upper.get() ) {
               throw new IllegalArgumentException(
                         "can't set lower to " + i + " > upper");
          }
          lower.set(i);
     }
     public void setUpper(int i) {
          if(i < lower.get())
               throw new IllegalArgumentException(
                         "can't set upper to " + i + " < lower");
     }
     public boolean isInRange(int i) {
          return ( i >= lower.get() && i <= upper.get( ) );
     }
}

如果某个类含有复合操作,例如NumberRanger,那么近依靠委托并不足以实现线程安全性。在这种情况下,这个类必须提供自己的加锁机制以保证这些复合操作都是原子操作,除非整个复合操作都可以委托给状态变量。

如果一个状态变量是线程安全的,并且没有任何不变性条件来约束它的值,在变量的操作上也不存在任何不允许的状态转换,那么就可以安全地发布这个变量。

例:我们通过修改之前车辆追踪器的例子来示范如何发布一个底层变量

@ThreadSafe
public class SafePoint {
     @GuardedBy("this") private int x, y ;
     private SafePoint(int []a){ this(a[0], a[1]); }
     public SafePoint(SafePoint p) { this(p.get() ); }
     public SafePoint(int x, int y) {
          this.x = x;
          this.y = y;
     }
     public synchronized int[] get() {
          return new int[] {x, y};
     }
     public synchronized void set(int x, int y) {
          this.x = x;
          this.y = y;
     }
}
@ThreadSafe
public class PublishingVehicleTracker {
     private final ConcurrentMap<String, Point> locations;
     public DelegatingVehicleTracker(Map<String, Point> points) {
          locations = new ConcurrentHashMap<String, Point>(points);
     }
     public Map<String, Point> getLocations() {
          return Collections.unmodifiableMap (
               new HashMap<String, Point>(locations)
          };
     }
     public Point getLocation(String id) {
          return locations.get(id);
     }
     public void setLocation(String id, int x, int y) {
          if(!location.containsKey(id) )
               throw new IllegalArgumentException(
                    "invalid vechicle name:  " + id);
          locations.get(id).set(x, y);
     }
}  

从现有的线程安全类中添加功能:

Java类库包含许多有用的“基础模块”类。有时候,某个现成的线程安全类能支持我们需要的所有操作,但更多时候,现有的类智能支持大部分的操作,此时就需要在不破坏线程安全性的情况下添加一个新的操作。

例如:假设需要一个线程安全的链表,它需要提供一个原子的"若没有则添加(Put-If-Absent)"的操作。要添加一个新的原子操作,最安全的方法是修改原始的类,但这通常无法做到。另一个方法是扩展这个类:

@ThreadSafe
public class BetterVector<E> extends Vector<E> {
     public synchronized boolean putIfAbsent(E x) {
          boolean absent = !contains(x);
          if(absent)
               add(x);
          return absent;
     }
}     

 客户端加锁机制:

第三种策略是扩展类的功能,但并不是扩展类本身,而是将扩展代码放入一个"辅助类"中。

例 - 通过客户端加锁来实现"若没有则添加"

@ThreadSafe
public class ListHelper<E> {
     public List<E> list =
               Collections.synchronizedList( new ArrayList<E>( ) );
     ...
     public boolean putIfAbsent(E x) {
          synchronized(list) {
                boolean absent = !list.contains(x);
                if(absent)
                    list.add(x);
                return absent;
           }
     }
}

对于使用某个对象X的客户端代码,使用X本身用于保护其状态的锁来保护这段客户代码。要使用客户端加锁,你必须知道对象X使用的是哪一个锁。

组合:

     当为现有的类添加一个原子操作时,有一种更好的方法:组合。下面例子中ImprovedList通过将List对象的操作委托给底层的List实例来实现List的操作,同时还添加了一个原子的putIfAbsent方法。

@ThreadSafe
public class ImprovedList<T> implements List<T> {
     private final List<T> list;
     public ImprovedList(List<T> list) { this.list = list;}
     public synchronized boolean putIfAbsent(T x) {
          boolean contains = list.contains(x);
          if(contains)
               list.add(x);
          return !contains;
     }
     public synchronized void clear() { list.clear( ) ;}
     //...按照类似的方式委托List的其他方法
}

ImprovedList通过自身的内置锁增加了一层额外的加锁,额外的同步层可能导致轻微的性能损失,但损失还是很小的。(因为在底层List上不会再存在竞争)

时间: 2024-12-29 04:12:25

java线程(四) : 对象的组合的相关文章

【Thread】java线程之对象锁、类锁、线程安全

说明: 1.个人技术也不咋滴.也没在项目中写过线程,以下全是根据自己的理解写的.所以,仅供参考及希望指出不同的观点. 2.其实想把代码的github贴出来,但还是推荐在初学的您多亲自写一下,就没贴出来了. 一.基本说明 类.对象:...(不知道怎么说,只可意会不可言传>.<!):要明白哪些方法.变量是对象的,哪些是类的. 类锁.对象锁:对应类和对象.每个类有且仅有一个类锁,每个对象有且仅有一个对象锁. ex: Person p1 = new Person(); Person p2 = new

java线程四种状态

一个线程可以有四种状态: 1.新(new), 即线程刚刚创建,而并未执行 2.可运行(runnable),意味着一旦时间分片机制有空闲的CPU周期提供给一个线程,那个线程便可立即开始运行.因此,线程可能在.也可能不在运行当中,但一旦条件许可,没有什么能阻止它的运行——它既没有“死”掉,也未被“堵塞”. 3.Dead,从自己的run()方法中返回后,一个线程便已“死”掉.亦可调用 stop()令其死掉,但会产生一个违例——属于Error的一个子类(也就是说,我们通常不捕获它).记住一个违例的“掷”

java 线程四

/*简单的卖票程序,实现多个窗口同时卖票*/ /* 创建现成的第二种方法:实现Runnable接口 步骤: 1,定义类实现Runnable接口.Runnable接口中只有一个抽象方法,就是run方法. 2,覆盖Runnable接口中的run方法. 将线程要运行的代码放在此run方法中. 3,通过Thread类建立线程对象. 4,将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数. 为什么要将Runnable接口的子类对象传递给Thread的构造函数? 因为自定义的run方

java 线程之对象的同步和异步

一.多线程环境下的同步与异步 同步:A线程要请求某个资源,但是此资源正在被B线程使用中,因为同步机制存在,A线程请求不到,怎么办,A线程只能等待下去. package com.jalja.org.thread.demo01; public class Thread02 { public synchronized void method1(){ System.out.println("method1:"+Thread.currentThread().getName()); try { T

Java线程专栏文章汇总(转)

原文:http://blog.csdn.net/ghsau/article/details/17609747 JDK5.0之前传统线程        Java线程(一):线程安全与不安全 Java线程(二):线程同步synchronized和volatile Java线程(三):线程协作-生产者/消费者问题 Java线程(四):线程中断.线程让步.线程睡眠.线程合并 Java线程(五):Timer和TimerTask JDK5.0之后并发包        Java线程(六):线程池 Java线程

Java线程专栏文章汇总

JDK5.0之前传统线程        Java线程(一):线程安全与不安全 Java线程(二):线程同步synchronized和volatile Java线程(三):线程协作-生产者/消费者问题 Java线程(四):线程中断.线程让步.线程睡眠.线程合并 Java线程(五):Timer和TimerTask JDK5.0之后并发包        Java线程(六):线程池 Java线程(七):Callable和Future Java线程(八):锁对象Lock-同步问题更完美的处理方式 Java

java线程管理

java线程管理 参见: http://harmony.apache.org/subcomponents/drlvm/TM.html 1. 修订历史 2. 关于本文档 2.1. 目的 2.2. 面向的读者 2.3. 文档约定 2.4. 文档使用 3. 概览 3.1. 主要特点 3.2. VM中的线程管理器 3.3. 可移植性 4. 体系结构 4.1. 对外接口 4.1.1. Native 接口 4.1.2. Java* 接口 4.2. 数据结构 4.3. 线程控制结构 4.3.1.Native

《Java并发编程实战》第四章 对象的组合 读书笔记

一.设计线程安全的类 在设计线程安全类的过程中,须要包括下面三个基本要素: . 找出构成对象状态的全部变量. . 找出约束状态变量的不变性条件. . 建立对象状态的并发訪问管理策略. 分析对象的状态,首先从对象的域開始. 变量按作用域划分: . 全局变量 . 局部变量 . 方法行參 . 异常处理參数 1. 收集同步需求 假设不了解对象的不变性条件与后验条件,那么就不能确保线程安全性.要满足在状态变量的有效值或状态转换上的各种约束条件.就须要借助原子性和封装性. 说的更简略些是Java线程安全都是

java并发编程实战学习笔记之对象的组合与基础构建模块

第四章 对象的组合 4.1 构建安全的类 4.2 实例封闭 @ThreadSafe public class PersonSet {     @GuardedBy("this") private final Set<Person> mySet = new HashSet<Person>();     public synchronized void addPerson(Person p) {         mySet.add(p);     }     pub