`
aswang
  • 浏览: 837901 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

一个看似线程安全的示例

    博客分类:
  • java
阅读更多

在《java并发编程实战》第四章4.4.1节给出了一个程序示例:

 

@NotThreadSafe
class BadListHelper <E> {
    public List<E> list = Collections.synchronizedList(new ArrayList<E>());

    public synchronized boolean putIfAbsent(E x) {
        boolean absent = !list.contains(x);
        if (absent)
            list.add(x);
        return absent;
    }
}

 

  这个示例希望实现的功能是为List提供一个原子操作:若没有则添加。因为ArrayList本身不是线程安全的,所以通过集合Collections.synchronizedList将其转换为一个线程安全的类,然后通过一个辅助的方法来为List实现这么个功能。初看起来这个方法没问题,因为也添加了synchronized关键字实现加锁了。

 

但是仔细分析,你会发现问题。首先对于synchronized关键字,需要说明的是,它是基于当前的对象来加锁的,上面的方法也可以这样写:

 

public boolean putIfAbsent(E x) {
	synchronized(this) {
		boolean absent = !list.contains(x);
		if (absent)
			list.add(x);
		return absent;
    }
}

 

  所以这里的锁其实是BadListHelper对象, 而可以肯定的是Collections.synchronizedList返回的线程安全的List内部使用的锁绝对不是BadListHelper的对象,所以BadListHelper中的putIfAbsent方法和线程安全的List使用的不是同一个锁,因此上面的这个加了synchronized关键字的方法依然不能实现线程安全性。

 

下面给出书中的另一种正确的实现:

 

 

@ThreadSafe
class GoodListHelper <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;
        }
    }
}

 

  如果你要分析这个实现是否正确,你需要搞清楚Collections.synchronizedList返回的线程安全的List内部使用的锁是哪个对象,所以你得看看Collections.synchronizedList这个方法的源码了。该方法源码如下:

 

public static <T> List<T> synchronizedList(List<T> list) {
	return (list instanceof RandomAccess ?
                new SynchronizedRandomAccessList<T>(list) :
                new SynchronizedList<T>(list));
    }
 

 

通过源码,我们还需要知道ArrayList是否实现了RandomAccess接口:

 

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable

 

  查看ArrayList的源码,可以看到它实现了RandomAccess,所以上面的synchronizedList放回的应该是SynchronizedRandomAccessList的实例。接下来看看SynchronizedRandomAccessList这个类的实现:

 

 

static class SynchronizedRandomAccessList<E>
	extends SynchronizedList<E>
	implements RandomAccess {

        SynchronizedRandomAccessList(List<E> list) {
            super(list);
        }

	SynchronizedRandomAccessList(List<E> list, Object mutex) {
            super(list, mutex);
        }

	public List<E> subList(int fromIndex, int toIndex) {
	    synchronized(mutex) {
                return new SynchronizedRandomAccessList<E>(
                    list.subList(fromIndex, toIndex), mutex);
            }
        }

        static final long serialVersionUID = 1530674583602358482L;

        /**
         * Allows instances to be deserialized in pre-1.4 JREs (which do
         * not have SynchronizedRandomAccessList).  SynchronizedList has
         * a readResolve method that inverts this transformation upon
         * deserialization.
         */
        private Object writeReplace() {
            return new SynchronizedList<E>(list);
        }
    }
 

因为SynchronizedRandomAccessList这个类继承自SynchronizedList,而大部分方法都在SynchronizedList中实现了,所以源码中只包含了很少的方法,但是通过subList方法,我们可以看到这里使用的锁对象为mutex对象,而mutex是在SynchronizedCollection类中定义的,所以再看看SynchronizedCollection这个类中关于mutex的定义部分源码:

static class SynchronizedCollection<E> implements Collection<E>, Serializable {
	// use serialVersionUID from JDK 1.2.2 for interoperability
	private static final long serialVersionUID = 3053995032091335093L;

	final Collection<E> c;  // Backing Collection
	final Object mutex;     // Object on which to synchronize

	SynchronizedCollection(Collection<E> c) {
            if (c==null)
                throw new NullPointerException();
	    this.c = c;
            mutex = this;
        }
	SynchronizedCollection(Collection<E> c, Object mutex) {
	    this.c = c;
            this.mutex = mutex;
        }
}

  可以看到mutex就是当前的SynchronizedCollection对象,而SynchronizedRandomAccessList继承自SynchronizedList,SynchronizedList又继承自SynchronizedCollection,所以SynchronizedRandomAccessList中的mutex也就是SynchronizedRandomAccessList的this对象。所以在GoodListHelper中使用的锁list对象,和SynchronizedRandomAccessList内部的锁是一致的,所以它可以实现线程安全性。

 

 

分享到:
评论
1 楼 shanpao1234560 2016-09-07  
这个list不是静态的第一种情况下也会有线程安全的问题么,求指教

相关推荐

    多线程示例源码工程文件

    这是一个多线程的示例工程,非常适合新手学习! 这是一个多线程的示例工程,非常适合新手学习! 这是一个多线程的示例工程,非常适合新手学习! 这是一个多线程的示例工程,非常适合新手学习! 这是一个多线程的示例...

    用C#语言编写的一个多线程示例

    C#多线程示例C#多线程示例C#多线程示例C#多线程示例C#多线程示例C#多线程示例C#多线程示例C#多线程示例C#多线程示例C#多线程示例C#多线程示例C#多线程示例C#多线程示例

    Java 多线程 订票 示例 线程安全

    Java 多线程 订票 示例 线程安全 Demo 例子 经典

    多线程编程示例

    简单多线程编程入门,可以帮助你实现第一个多线程程序

    VC++ 线程优先级 示例程序

    VC++ 线程优先级 示例程序 VC++线程优先级示例程序-赛马,不同优先级执行的程序显示在进度条中,有长有短,像赛马一样,体现出每个线程被执行的优先顺序。

    VisualC 实效编程 61 线程优先级示例-赛马

    VisualC 实效编程 61 线程优先级示例-赛马VisualC 实效编程 61 线程优先级示例-赛马VisualC 实效编程 61 线程优先级示例-赛马VisualC 实效编程 61 线程优先级示例-赛马VisualC 实效编程 61 线程优先级示例-赛马...

    c# 线程安全队列的用法原理及使用示例

    答:线程安全是多线程编程时的计算机程序代码中的一个概念。在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况。 在多线程...

    多线程优先级示例_赛马

    多核课程 多线程优先级示例_赛马 通过例子来演示多线程优先级

    servlet线程安全问题

    servlet线程安全问题servlet线程安全问题

    java多线程示例

    Thread本质上也是一个实现了Runnable的实例,他代表一个线程的实例,并且启动线程的唯一方法就是通过Thread类的start方法。 2.实现Runnable接口,并实现该接口的run()方法.创建一个Thread对象,用实现的Runnable接口...

    c#线程安全的源代码示例

    这是一个源代码项目,通过很明了的代码来向大家诠释什么叫做线程安全,怎样才能达到线程安全,已经怎样去避免非线程安全问题

    C# winform多线程模板示例,winform多线程例子,C#

    这是一个C# winform的多线程操作示例,可以参考解决界面卡死问题

    等线程示例学习等线程示例学习

    等线程示例学习等线程示例学习等线程示例学习等线程示例学习等线程示例学习等线程示例学习

    C#-多线程排序示例

    C# 的多线程排序示例 C# 的多线程排序示例 C# 的多线程排序示例 C# 的多线程排序示例 适合初学者使用

    mysql是线程不安全的,mysql不是线程安全的,多线程共用同一个mysql连接是会崩溃的.所以同样QT的QSqlDatabase也是线程不安全的,QS会崩溃

    mysql是线程不安全的,mysql不是线程安全的,多线程共用同一个mysql连接是会崩溃的 QT的QSqlDatabase是基于mysql的,所以一样是线程不安全的 现讲明mysql为什么是线程不安全的,以及在多线程环境下如何使用mysql,...

    【C#】使用.net 4.0开发的多线程运行示例

    示例设置日志线程每个1分钟收集,3个任务线程处理的日志,写入log文件到本地。 3个任务线程在处理完任务后,继续开启新线程进行任务处理,但总数不超过设置的线程阀值。示例阀值为3. 喜欢多线程的朋友,不妨一起...

    今天没事给大家写一个多线程例子

    今天没事给大家写一个多线程例子 今天没事给大家写一个多线程例子

    C++线程安全队列_示例代码.zip

    【C++ 语言】线程安全队列 ( 条件变量 | 线程调度 ) : https://hanshuliang.blog.csdn.net/article/details/102851323 下载完项目后 , 使用 Visual Studio 打开 , 注意需要配置 POSIX 线程库 ( 参考以下博客配置...

    多线程示例

    MFC窗口程序多线程示例 MFC窗口程序多线程示例 MFC窗口程序多线程示例

    Asp.net多线程下载示例源码

    支持多线程: 多个线程某时刻下载同一个文件的不同块. 2. 断点续传: 如果下载了一个文件的某些块(一半), 则下次下载时只需下载未完成的块;文件块的下载状态用控制文件记录. 块下载完成的先后顺序不一定是连续的....

Global site tag (gtag.js) - Google Analytics