在JAVA中ArrayList如何保证线程安全
[b]保证线程安全的三种方法:[/b]
不要跨线程访问共享变量
使共享变量是final类型的
将共享变量的操作加上同步
一开始就将类设计成线程安全的, 比在后期重新修复它,更容易.
编写多线程程序, 首先保证它是正确的, 其次再考虑性能.
无状态或只读对象永远是线程安全的.
不要将一个共享变量裸露在多线程环境下(无同步或不可变性保护)
多线程环境下的延迟加载需要同步的保护, 因为延迟加载会造成对象重复实例化
对于volatile声明的数值类型变量进行运算, 往往是不安全的(volatile只能保证可见性,不能保证原子性).详见volatile原理与技巧中, 脏数据问题讨论.
当一个线程请求获得它自己占有的锁时(同一把锁的嵌套使用), 我们称该锁为可重入锁.在jdk1.5并发包中, 提供了可重入锁的java实现-ReentrantLock.
每个共享变量,都应该由一个唯一确定的锁保护.创建与变量相同数目的ReentrantLock, 使他们负责每个变量的线程安全.
虽然缩小同步块的范围, 可以提升系统性能.但在保证原子性的情况下, 不可将原子操作分解成多个synchronized块.
在没有同步的情况下, 编译器与处理器运行时的指令执行顺序可能完全出乎意料.原因是, 编译器或处理器为了优化自身执行效率, 而对指令进行了的重排序(reordering).
当一个线程在没有同步的情况下读取变量, 它可能会得到一个过期值, 但是至少它可以看到那个线程在当时设定的一个真实数值. 而不是凭空而来的值. 这种安全保证, 称之为最低限的安全性(out-of-thin-air safety)
在开发并发应用程序时, 有时为了大幅度提高系统的吞吐量与性能, 会采用这种无保障的做法.但是针对, 数值的运算, 仍旧是被否决的.
volatile变量,只能保证可见性, 无法保证原子性.
某些耗时较长的网络操作或IO, 确保执行时, 不要占有锁.
发布(publish)对象, 指的是使它能够被当前范围之外的代码所使用.(引用传递)对象逸出(escape), 指的是一个对象在尚未准备好时将它发布.
原则: 为防止逸出, 对象必须要被完全构造完后, 才可以被发布(最好的解决方式是采用同步)
this关键字引用对象逸出
例子: 在构造函数中, 开启线程, 并将自身对象this传入线程, 造成引用传递.而此时, 构造函数尚未执行完, 就会发生对象逸出了.
必要时, 使用ThreadLocal变量确保线程封闭性(封闭线程往往是比较安全的, 但一定程度上会造成性能损耗)封闭对象的例子在实际使用过程中, 比较常见, 例如 hibernate openSessionInView机制, jdbc的connection机制.
单一不可变对象往往是线程安全的(复杂不可变对象需要保证其内部成员变量也是不可变的)良好的多线程编程习惯是: 将所有的域都声明为final, 除非它们是可变的
保证共享变量的发布是安全的a, 通过静态初始化器初始化对象(jls 12.4.2叙述, jvm会保证静态初始化变量是同步的) b, 将对象申明为volatile或使用AtomicReference c, 保证对象是不可变的d, 将引用或可变操作都由锁来保护
设计线程安全的类, 应该包括的基本要素: a, 确定哪些是可变共享变量b, 确定哪些是不可变的变量c, 指定一个管理并发访问对象状态的策略
将数据封装在对象内部, 并保证对数据的访问是原子的.建议采用volatile javabean模型或者构造同步的getter,setter.
线程限制性使构造线程安全的类变得更容易, 因为类的状态被限制后, 分析它的线程安全性时, 就不必检查完整的程序.
编写并发程序, 需要更全的注释, 更完整的文档说明.
在需要细分锁的分配时, 使用java监视器模式好于使用自身对象的监视器锁.前者的灵活性更好.
Object target = new Object();
// 这里使用外部对象来作为监视器, 而非this
synchronized(target) {
// TODO
}
针对java monitor pattern, 实际上ReentrantLock的实现更易于并发编程.功能上, 也更强大.
设计并发程序时, 在保证伸缩性与性能折中的前提下, 优先考虑将共享变量委托给线程安全的类.由它来控制全局的并发访问.
使用普通同步容器(Vector, Hashtable)的迭代器, 需要外部锁来保证其原子性.原因是, 普通同步容器产生的迭代器是非线程安全的.
在并发编程中, 需要容器支持的时候, 优先考虑使用jdk并发容器(ConcurrentHashMap, ConcurrentLinkedQueue, CopyOnWriteArrayList...).
ConcurrentHashMap, CopyOnWriteArrayList并发容器的迭代器,以及全范围的size(), isEmpty() 都表现出弱一致性.他们只能标示容器当时的一个数据状态. 无法完整响应容器之后的变化和修改.
使用有界队列, 在队列充满或为空时, 阻塞所有的读与写操作. (实现生产-消费的良好方案) BlockQueue下的实现有LinkedBlockingQueue与ArrayBlockingQueue, 前者为链表, 可变操作频繁优先考虑,后者为数组, 读取操作频繁优先考虑. PriorityBlockingQueue是一个按优先级顺序排列的阻塞队列, 它可以对所有置入的元素进行排序(实现Comparator接口)
当一个方法, 能抛出InterruptedException, 则意味着, 这个方法是一个可阻塞的方法, 如果它被中断, 将提前结束阻塞状态.当你调用一个阻塞方法, 也就意味着, 本身也称为了一个阻塞方法, 因为你必须等待阻塞方法返回.
如果阻塞方法抛出了中断异常, 我们需要做的是, 将其往上层抛, 除非当前已经是需要捕获异常的层次.如果当前方法, 不能抛出InterruptedException, 可以使用Thread.currentThread.interrupt()方法, 手动进行中断.
在JAVA中ArrayList如何保证线程安全的更多相关文章
- java中volatile不能保证线程安全
今天打了打代码研究了一下java的volatile关键字到底能不能保证线程安全,经过实践,volatile是不能保证线程安全的,它只是保证了数据的可见性,不会再缓存,每个线程都是从主存中读到的数据,而 ...
- Java中的进程和线程
Java中的进程与线程 一:进程与线程 概述:几乎任何的操作系统都支持运行多个任务,通常一个任务就是一个程序,而一个程序就是一个进程.当一个进程运行时,内部可能包括多个顺序执行流,每个顺序执行流就是 ...
- java中ArrayList 、LinkList区别
转自:http://blog.csdn.net/wuchuanpingstone/article/details/6678653 个人建议:以下这篇文章,是从例子说明的方式,解释ArrayList.L ...
- java中的++i是线程安全的吗?
java中的++i是线程安全的吗?为什么?怎么使它线程安全呢? 先说答案: 非线程安全 先说下为什么是非线程安全的? 从Java内存模型说起 Java内存模型规定了所有的便利都存储在主内存中,每个线程 ...
- Java中的进程与线程(总结篇)
详细文档: Java中的进程与线程.rar 474KB 1/7/2017 6:21:15 PM 概述: 几乎任何的操作系统都支持运行多个任务,通常一个任务就是一个程序,而一个程序就是一个进程.当一个进 ...
- JAVA中ArrayList用法
JAVA中ArrayList用法 2011-07-20 15:02:03| 分类: 计算机专业 | 标签:java arraylist用法 |举报|字号 订阅 Java学习过程中做题时 ...
- Java中ArrayList与LinkedList的区别
Java中ArrayList与LinkedList的区别 一般大家都知道ArrayList和LinkedList的区别: 1. ArrayList的实现是基于数组,LinkedList的实现是基于双向 ...
- Java中arraylist和linkedlist源代码分析与性能比較
Java中arraylist和linkedlist源代码分析与性能比較 1,简单介绍 在java开发中比較经常使用的数据结构是arraylist和linkedlist,本文主要从源代码角度分析arra ...
- JAVA 中无锁的线程安全整数 AtomicInteger介绍和使用
Java 中无锁的线程安全整数 AtomicInteger,一个提供原子操作的Integer的类.在Java语言中,++i和i++操作并不是线程安全的,在使用的时候, 不可避免的会用到synchron ...
随机推荐
- 解决html表格中内容超出不强制换行和超出宽度自动隐藏并显示省略号
在表格布局中经常会遇到因为表格内容长短的变化导致布局混乱的情况,这个时候我们可能会有为了布局稳定把单元格宽度写死的情况:但是我们设置了宽度却发现超出了宽度之后会自动变大,用css定义元素的overfl ...
- js判断空对象
最近项目遇到判断空对象的一个问题,查阅相关资料再进行总结一下. 判断空对象不比判断空字符串之类的,因为空对象也是一个对象,需要单独分配内存,而不是像字符串那样为空时就是大锅饭,大家都相等,如下代码: ...
- ubuntu 12.04 react-native 安装
1.安装nodejs 和npm apt-get install nodejs apt-get install npm 2. 升级node js 和npm sudo npm cache clean -f ...
- 操作系统开发系列—12.a.从Loader到内核 ●
Loader要做两项工作,我们先来做第一项,把内核加载到内存: 1.加载内核到内存. 2.跳入保护模式. 首先编译无内核时: nasm boot.asm -o boot.bin nasm loader ...
- Vysor:在电脑里控制你的安卓手机
Vysor是一款可以使用电脑操作安卓手机的谷歌浏览器插件,在chrome中安装了Vysor插件以后,用户就可以使用电脑打开手机的屏幕,在手机的屏幕界面中,用户不仅可以观看,甚至可以使用电脑直接操作手机 ...
- 我的Android第三章:Android的组件介绍
小编摘录了Android文档介绍Android四大组件的基本内容,感觉文档的内容写的很详细所以小编将它写入了博客 Android 使用Java语言开发.Android SDK 工具编译代码-以及任意数 ...
- 【代码笔记】iOS-截屏功能
代码: - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. // ...
- 【读书笔记】iOS网络-同步请求,队列式异步请求,异步请求的区别
一,同步请求的最佳实践. 1,只在后台过程中使用同步请求,除非确定访问的是本地文件资源,否则请不要在主线程上使用. 2,只有在知道返回的数据不会超出应用的内存时才使用同步请求.记住,整个响应体都会位于 ...
- iOS开发之多线程技术
本篇争取一篇讲清讲透,依然将通过四大方面清晰的对iOS开发中多线程的用法进行详尽的讲解: 一.什么是多线程 1)多线程执行原理 2)线程与进程 3)多线程的优缺点 二.我们为什么要用多线程编程技术 三 ...
- 从零开始一个iOS项目(一)——基本准备以及cocopods的安装
项目开发分为:独立开发和迭代开发,我主要讲独立开发,若读者是迭代开发,希望公司的代码一定要规范,便能省去许多麻烦,也能从中获益,闲时也可接些外包,赚一些外快,也是美差,然而独立开发最能锻炼人的能力. ...