hidden mutable state和escaped state是两种线程不安全问题:两者原因不同,前者主要是由于类成员变量中含有其他对象的引用,而这个引用是immutable的;后者是成员方法的返回结果类型需要注意,否者都会引起线程安全问题

1、关于hidden mutable state问题:

注意成员变量如果是另一个对象的引用情况

这个问题简而言之就是说一个类的成员变量有可能是暗含状态的,就是说成员是一个对象的引用哪个对象是有状态的,虽然这个引用可能定义为final不可变的但依然不是线程安全的!

首先说明一个类的实例  如何被多线程程序执行?

 public class DateFormatTest {

 //这里说明成员变量的初始化会在构造函数之前进行!!

 private final DateFormat format =new SimpleDateFormat("yyyyMMdd");

   public Date convert(String source) throws ParseException{

     Date d = format.parse(source);

     return d;

   }

 }// 成员变量是immutable,又不提供方法改变这个成员,这个对象是immutable?no,因为成员变量DateFormat的问题

 //定义一个对象然后被多线程共用

 final DateFormatTest t = new DateFormatTest();

 Callable<Date> task = new Callable<Date>(){

     public Date call() throws Exception {

         return t.convert("20100811");

     }

 };

 //lets try 2 threads only

 ExecutorService exec = Executors.newFixedThreadPool(2);

 List<Future<Date>> results =new ArrayList<Future<Date>>();

 //perform 5 date conversions

 for(int i = 0 ; i < 5 ; i++){

    results.add(exec.submit(task));

 }

 exec.shutdown();

 //look at the results

 for(Future<Date> result : results){

     System.out.println(result.get());

 }

1.1、说明下这个类

This code is not thread safe because SimpleDateFormat.format is not.Is this object immutable? Good question! We have done our best to make all fields not modifiable, we don't use any setter or any methods that let us suggest that the state of the object will change. Actually, internally SimpleDateFormat changes its state, and that's what makes it non-thread safe. Since something changed in the object graph, I would say that it's not immutable, even if it looks like it...

1.2、hidden mutable state问题

SimpleDateFormat 这个类的实例对象中保存有 intermediate results,所以如果一个实例对象被多个线程执行就可能会混淆彼此的执行结果。----该类有成员变量

class SimpleDateFormat{

private Calendar  calendar;

….

public void parse(){

calendar.clear();//清空

calendar.add(..);//填充

}

}

SimpleDateFormat stores intermediate results in instance fields. So if one instance is used by two threads they can mess each other's results.

Looking at the source code reveals that there is a Calendar instance field, which is used by operations on DateFormat / SimpleDateFormat

For example parse(..) calls calendar.clear() initially and then calendar.add(..). If another thread invokes parse(..) before the completion of the first invocation, it will clear the calendar, but the other invocation will expect it to be populated with (被填充)intermediate results of the calculation.

One way to reuse date formats without trading thread-safety is to put them in a ThreadLocal - some libraries do that. That's if you need to use the same format multiple times within one thread. But in case you are using a servlet container (that has a thread pool), remember to clean the thread-local after you finish.

1.3、解决办法可以做thread local

Another approach is to use a ThreadLocal variable to hold the DateFormat object, which means that each thread will have its own copy and doesn't need to wait for other threads to release it. This is generally more efficient than synchronising sa in the previous approach.

public class DateFormatTest {

  private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>(){

    @Override

    protected DateFormat initialValue() {

        return new SimpleDateFormat("yyyyMMdd");

    }

  };

  public Date convert(String source) throws ParseException{

    Date d = df.get().parse(source);

    return d;

  }

}

2、关于escaped state问题:

注意函数的返回结果!!!

 public class Tournament {

 private List<Player> players = new LinkedList<Player>();

 public synchronized void addPlayer(Player p) {

 players.add(p);

 }

 public synchronized Iterator<Player> getPlayerIterator() {

 return players.iterator();

 }

 }

比如该类成员的所有操作都是synchronize方法做的,成员是可变的,但是也不是线程安全的!

要考虑每个方法返回的结果是否依旧可以操纵这个对象的可变状态!

问题在于:函数getPlayerIterator() 的返回结果iterator still references the mutable state contained within players—if another thread calls addPlayer() while the iterator is in use

3、类成员是一个对象,并且在该类的 synchronized方法中去掉用这个对象的方法会有隐患

如果成员方法中已经获得了锁,然后调用成员变量的方法有可能再次申请锁就会出现取得锁又申请锁,这个时候用copyonwrite数据结构

 class Downloader extends Thread {
private InputStream in;
private OutputStream out; //该类中有一个成员变量是其他对象的引用
private ArrayList<ProgressListener> listeners; public synchronized void addListener(ProgressListener listener) {
listeners.add(listener);
}
public synchronized void removeListener(ProgressListener listener) {
listeners.remove(listener);
} //这里存在隐患,因为一个synchronized方法(表示得到了一把锁),然后调用另一个对象的方法,当时你不知道这个方法中是否会再次用到锁,这里就会有隐患 private synchronized void updateProgress(int n) {
for (ProgressListener listener: listeners)
➤ listener.onProgress(n);
} 。。。。。。 }

解决方法就是得到锁的时候避免调用另一个可能获取锁的方法,做次拷贝好了!!!

修改成员方法如下:去掉成员方法的synchronized关键字,然后作clone

 private void updateProgress(int n) {
ArrayList<ProgressListener> listenersCopy;
synchronized(this) {
➤ listenersCopy = (ArrayList<ProgressListener>)listeners.clone();
}
for (ProgressListener listener: listenersCopy)
listener.onProgress(n);
}

其实java已经提供了所谓的copyonwrite集合,来应付这中情形,在做iterator集合的时候并不copy而是在修改的时候才做copy

CopyOnWrite容器即写时复制的容器。通俗的理解是当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。这样做的好处是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。

http://coolshell.cn/articles/11175.html

三种线程不安全现象描述(escaped state以及hidden mutable state)的更多相关文章

  1. Reactor三种线程模型与Netty线程模型

    文中所讲基本都是以非阻塞IO.异步IO为基础.对于阻塞式IO,下面的编程模型几乎都不适用 Reactor三种线程模型 单线程模型 单个线程以非阻塞IO或事件IO处理所有IO事件,包括连接.读.写.异常 ...

  2. java面试记录二:spring加载流程、springmvc请求流程、spring事务失效、synchronized和volatile、JMM和JVM模型、二分查找的实现、垃圾收集器、控制台顺序打印ABC的三种线程实现

    注:部分答案引用网络文章 简答题 1.Spring项目启动后的加载流程 (1)使用spring框架的web项目,在tomcat下,是根据web.xml来启动的.web.xml中负责配置启动spring ...

  3. 非递归实现二叉树的三种遍历操作,C++描述

    body, table{font-family: 微软雅黑; font-size: 13.5pt} table{border-collapse: collapse; border: solid gra ...

  4. IOS 多线程,线程同步的三种方式

    本文主要是讲述 IOS 多线程,线程同步的三种方式,更多IOS技术知识,请登陆疯狂软件教育官网. 一般情况下我们使用线程,在多个线程共同访问同一块资源.为保护线程资源的安全和线程访问的正确性. 在IO ...

  5. HDL的三种描述方式

    结构化描述 结构化描述方式是最原始的描述方式,是抽象级别最低的描述方式,但同时也是最接近于实际的硬件结构的描述方式.结构化的描述方式,思路就像在面包板上搭建数字电路一样,唯一的不同点就是我们通过HDL ...

  6. java多线程三之线程协作与通信实例

    多线程的难点主要就是多线程通信协作这一块了,前面笔记二中提到了常见的同步方法,这里主要是进行实例学习了,今天总结了一下3个实例: 1.银行存款与提款多线程实现,使用Lock锁和条件Condition. ...

  7. java 多线程编程三种实现方式

    一种是继承Thread类,一种是实现Runable接口,还有一种是实现callable接口: 有博主说只有前面2种方式,我个人愚见是三种,主要详细介绍下callable的使用: 三种线程的我的个人理解 ...

  8. JDK提供的四种线程池代码详解

    一.线程池什么时候使用,会给我们带来什么好处? 如果很多用户去访问服务器,用户访问服务器的时间是非常短暂的,那么有可能在创建线程和销毁线程上花费的时间会远远大于访问所消耗的时间,如果采用线程池会使线程 ...

  9. ThreadPoolExecutor使用和思考(上)-线程池大小设置与BlockingQueue的三种实现区别

    工作中多处接触到了ThreadPoolExecutor.趁着现在还算空,学习总结一下. 前记: jdk官方文档(javadoc)是学习的最好,最权威的参考. 文章分上中下.上篇中主要介绍ThreadP ...

随机推荐

  1. IOS登陆+注册+抽奖+排行榜

    要求:三个页面(登录页面,pickerView页面,排行榜页面),pickerView页面是三个组件,每个组件显示0-9,点击按钮进行随机,获得的值存入排行榜,排行榜显示大于500的最高的10个分数和 ...

  2. 如何解决虚拟机Mac OS X 不支持二进制编译问题()

    本文将着重解决在使用VMware 11安装Mac OS虚拟机出现”Mac OS X 不支持二进制编译.若要运行 Mac OS X 主机上需要一个 VMware Workstation 支持英特尔 VT ...

  3. 【MongoDB】2.可视化工具的安装和使用

    首先:关于  能支持MongoDB新版本的可视化工具,争议不断,各人都有各人的支持. 因此之前选择安装的时候就安装了MongoDB  3.0.14版本的. 最终,确定使用Robomongo作为我第一个 ...

  4. Java利用POI导入导出Excel中的数据

         首先谈一下今天发生的一件开心的事,本着一颗android的心我被分配到了PB组,身在曹营心在汉啊!好吧,今天要记录和分享的是Java利用POI导入导出Excel中的数据.下面POI包的下载地 ...

  5. java 线程演示

    package unit8; public class Mainthread { public static void main(String[] args) { Thread t = new Thr ...

  6. 比较和排序(IComparable和IComparer以及它们的泛型实现)

    本文摘要: 1:比较和排序的概念: 2:IComparable和IComparer: 3:IComparable和IComparer的泛型实现IComparable<T>和ICompare ...

  7. javascript引擎工作原理

    1. 什么是JavaScript解析引擎? 简单地说,JavaScript解析引擎就是能够“读懂”JavaScript代码,并准确地给出代码运行结果的一段程序.比方说,当你写了 var a = 1 + ...

  8. hadoop 分布式缓存

    Hadoop 分布式缓存实现目的是在所有的MapReduce调用一个统一的配置文件,首先将缓存文件放置在HDFS中,然后程序在执行的过程中会可以通过设定将文件下载到本地具体设定如下: public s ...

  9. C# 词法分析器(七)总结

    系列导航 (一)词法分析介绍 (二)输入缓冲和代码定位 (三)正则表达式 (四)构造 NFA (五)转换 DFA (六)构造词法分析器 (七)总结 在之前的六篇文章中,我比较详细的介绍了与词法分析器相 ...

  10. BOM和DOM的区别和关联

    BOM 1. BOM是Browser Object Model的缩写,即浏览器对象模型. 2. BOM没有相关标准. 3. BOM的最根本对象是window. 从1可以看出来:BOM和浏览器关系密切. ...