- 浏览: 847658 次
- 性别:
- 来自: 南京
文章分类
最新评论
-
loveseed1989:
您好,我用您的方法运行Cone.java,会给我报java.l ...
vtk学习笔记 --- 编译vtk库和java库 -
60love5:
60love5 写道首先谢谢你的解析,但你这个验证可见性的小程 ...
多线程中共享对象的可见性 -
60love5:
首先谢谢你的解析,但你这个验证可见性的小程序是存在问题的,你的 ...
多线程中共享对象的可见性 -
Gamehu520:
...
java 中的Unsafe -
shanpao1234560:
这个list不是静态的第一种情况下也会有线程安全的问题么,求指 ...
一个看似线程安全的示例
在《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内部的锁是一致的,所以它可以实现线程安全性。
发表评论
-
基于Oracle Streams + Oracle AQ 捕获变更,发布变更(二)
2014-11-21 22:23 3214要求:使用Oracle Streams捕获某个用户下部 ... -
基于Oracle Streams + Oracle AQ 捕获变更,发布变更(一)
2014-11-20 22:23 2804要求:使用Oracle Streams捕获某个用户下部分表 ... -
如何去掉在浏览器中打开java applet时的警告对话框
2013-08-24 12:10 6996好久没更新博客了! 最近,由于项目要求,需要将sw ... -
Android 内存泄露笔记
2013-03-05 23:10 01、大部分内存泄露都是错误的持有了Activity或者Con ... -
java 虚拟机总结 【思维导图】
2012-12-22 20:11 2096java虚拟机总结思维导图: 参考《深入理解jav ... -
Java虚拟机字节码执行引擎 【思维导图】
2012-12-22 19:51 1685java虚拟机字节码执行引擎思维导图总结: 参考《深入理 ... -
java 垃圾回收相关总结 【思维导图】
2012-12-21 19:03 3286java垃圾回收相关总结: 参考《深入理解java ... -
java并发中的延迟初始化
2012-12-12 19:17 4706在《java并发编程实战 ... -
java同步容器与并发容器
2012-12-09 18:07 4363何为同步容器:可以简 ... -
多线程中的long和double
2012-12-08 19:26 4205在看一些代码的时候,会发现在定义long型和double型的 ... -
多线程中共享对象的可见性
2012-12-08 18:52 5149在阅读《java并发编程实战》的第三章的时候,看到书中的一个 ... -
一个快速、轻量级 Collection 库 Trove
2012-12-07 09:35 3764Trove一个快速、轻量级针对java原子类型(byte,i ... -
java 中的Unsafe
2012-12-05 22:25 32760在阅读AtomicInteger的源码时,看到了这个类:su ... -
在ubuntu10上编译Thrift0.8.0
2012-08-01 15:34 2158下载thrift0.8.0 ,地址: http:// ... -
生活小工具--记账小助手1.0发布
2012-05-31 15:08 1755因为自己平时喜欢记账,把每日的消费情况都记录下来,所以希望找 ... -
话费速查升级版v1.3发布
2012-05-04 12:47 1441前段时间开发了一个话费速查的小应用,最近一直保持每周更新一个 ... -
编译zeromq的java绑定:jzmq
2012-05-03 22:47 135511、 下载zeromq源码:http://www.ze ... -
Android软件包静默安装小应用 - 附源码
2012-04-21 20:50 12421老早之前,写了一个android软件包静默安装的应用,放在工 ... -
最方便的联通话费,余额查询软件来了! --- 联通话费速查v1.2
2012-04-12 18:01 4044软件介绍: 联通话费速查是一款针对联通 ... -
android开发之定制标题栏 --- 附源码
2012-04-11 21:53 12160在开发上个应用 话费 ...
相关推荐
在这个MFC多线程示例工程文件中,我们可以深入学习如何在Windows环境下利用MFC来创建和管理多线程程序。多线程是现代软件开发中一个重要的概念,它允许程序同时执行多个任务,从而提高系统的效率和响应性。 首先,...
首先,我们要理解一个虚假的多线程示例。在例1中,创建了两个`TestThread`对象,分别调用`go(0)`和`go(1)`方法。尽管每个方法都在无限循环中调用了`Thread.sleep(100)`,但预期的线程切换并未发生。这是因为`sleep()...
"多线程示例java"这个标题和描述指向了Java语言中实现多线程的实践案例。下面将详细讨论多线程的基本概念、Java中的线程创建方式以及多线程同步控制等相关知识点。 1. **线程的基本概念** - **线程**:线程是程序...
这种操作在单线程环境中是安全的,因为没有其他线程可能同时访问和修改同一个对象。 ```csharp private void Form1_Load(object sender, EventArgs e) { label1.Text = "100"; } ``` #### 知识点三:按钮点击事件...
通过创建一个新的线程来执行耗时操作,可以让主界面线程继续响应用户的输入事件。这样做的好处是提高了用户体验,使得程序不会因为后台任务而失去响应。 #### 四、Win32 API中的多线程支持 **创建线程:** ```cpp...
本资源"ConsoleApp1"是一个C#实现的多线程示例,它演示了如何在多线程环境中顺序打印1到100的数字。在多线程环境下实现顺序打印看似简单,实则涉及到线程同步和控制的问题,这通常通过各种同步原语来实现。在这个...
创建一个简单的循环计数函数`CountTime`,并在UI界面上添加一个按钮,单击该按钮时调用此函数。由于在单线程环境中,所有操作都在同一个线程中执行,因此在执行长时间操作时,UI会变得无响应。 ```cpp void ...
一个进程可以包含多个线程,这些线程看似同时执行,但实际上是通过操作系统调度来实现的。 - **线程与进程的区别**:进程是资源分配的基本单位,而线程则是处理器调度的基本单位。同一进程内的线程共享进程的资源...
在Java编程中,多线程是一项关键技能,它允许程序同时执行多个任务,提升系统效率。这个名为"Java多线程的小例子——吃包子"的示例...这个示例是一个生动的教学工具,能够帮助初学者更好地理解和运用Java的多线程技术。
这些文件可能是用来实现一个具体的线程示例,比如 `GetHtml.dcu` 可能包含了从网页获取数据的线程代码,而 `libeay32.dll`, `libssl32.dll` 和 `ssleay32.dll` 可能与 SSL/TLS 加密有关,用于安全地通过网络通信。...
线程是进程的一个实例,每个进程至少包含一个线程,但可以有多个线程,这些线程共享同一进程的资源,如内存空间、打开的文件等。 在多线程编程中,我们关注以下几个核心概念: 1. **线程创建**:程序可以通过创建...
在计算机科学领域,尤其是操作系统和并发编程中,"移动小球/多线程控制"是一个经典的教学示例,用于演示和理解多线程的概念。在这个实验中,我们看到一个简单的应用,它创建了三个小球并让它们按照特定的轨迹进行...
在一个单处理器系统中,操作系统通过时间片轮转的方式让多个线程看似同时运行,而在多处理器系统中,每个线程可以被分配到不同的处理器上,真正地并发执行。在Java中,我们有两种方式创建线程: 1. **扩展Thread类*...
以下是一个实现Runnable接口的多线程示例: ```java public class DoSomething implements Runnable { private String name; public DoSomething(String name) { this.name = name; } public void run() { for...
以下是一个使用Runnable接口实现多线程的简单示例: ```java public class Main { public static void main(String[] args) { Thread thread1 = new Thread(new DoSomething("Thread 1")); Thread thread2 = new ...
以下是一个简单的示例: ```java class Mythread extends Thread { @Override public void run() { for (int i = 0; i ; i++) { if (i % 2 == 0) { System.out.println(Thread.currentThread().getName() + ":...
1. **主线程被子线程终止:** 在一个示例中,主线程将它的线程ID传递给子线程,子线程随后尝试取消主线程。在这种情况下,虽然主线程会被终止,但由于进程仍在运行,子线程不会立即退出,而是继续执行其循环。如果子...