使用Immutable对象解决线程安全
来源:互联网 发布:北京周末好去处 知乎 编辑:程序博客网 时间:2024/05/22 14:56
何为Immutable对象?
简单地说,如果一个对象实例不能被更改就是一个Immutable的对象,Java SDK提供的大量值对象,比如String等都是Immutable的对象。
如何使对象Immutable?
按照Effective Java的说明,需要满足下面几条规则:
- 保证类不能被继承 - 为了避免其继承的类进行mutable的操作
- 移调所有setter/update等修改对象实例的操作
- 保证所有的field是private和final的
为什么要采用Immutable对象?
在并发程序中,使用Immutable可以既保证线程安全性,跟并发锁方式相比,它大大增强了并发时的效率。尤其当一个对象是值对象时,更应该考虑采用Immutable方式。
为了说明,这里先举一个Mutable的非线程安全的例子。person应该是一个典型的值对象,但下面的例子没有使他具备Immutable特性。
//non thread-safe
public class ImmutableDemo {static MutablePerson testM = new MutablePerson("joanieM", 14);public static void main(String[] args) {Thread t1 = new MutableTestThread(1);t1.start();Thread t2 = new MutableTestThread(2);t2.start();}}class MutablePerson {private int age; //Rule 1: all fields are private and finalprivate String name;public MutablePerson(String name, int age) { //rule 2: a factory method pattern is adopted to create the objectthis.age = age;this.name = name;}public String getName() {return name;}public int getAge() {return age;}public String toString() {return name +": "+age +"year(s) old";}public void updatePerson(String name, int age){this.name = name;this.age = age;System.out.println(this);}}class MutableTestThread extends Thread {final int MAX=10; final int idx;public MutableTestThread(int idx) {this.idx = idx;}public void run() {for (int i = 0; i < MAX; i++) {ImmutableDemo.testM.updatePerson("joanieM"+idx, idx*MAX+i);try {Thread.sleep(20+i*2);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}}
运行结果:
joanieM1: 10year(s) oldjoanieM2: 20year(s) old
joanieM2: 11year(s) old
joanieM2: 11year(s) old
joanieM2: 22year(s) old
joanieM2: 22year(s) old
joanieM2: 13year(s) old
joanieM2: 13year(s) old
joanieM2: 14year(s) old
joanieM2: 14year(s) old
joanieM1: 15year(s) old
joanieM2: 25year(s) old
joanieM1: 16year(s) old
joanieM1: 16year(s) old
joanieM2: 27year(s) old
joanieM1: 27year(s) old
joanieM1: 18year(s) old
joanieM2: 28year(s) old
joanieM2: 29year(s) old
joanieM1: 19year(s) old
结果中红颜色标注的都是错误的结果。由于没有采取任何保证线程安全性的操作,首先线程t1执行完updatePerson函数的this.age=age后被挂起,线程t2执行完updatePerson函数的this.name=name后被挂起,线程t1继续执行,此时的name值为t2执行的结果joanieM2,age则为t1执行的结果,于是打印出了错误的值:joanieM2: 11year(s) old
下面的例子给出了如何使用Immutable来保证值对象的线程安全性的。
public class ImmutableDemo {//test is a shared thread-safe object. static ImmutablePerson test = ImmutablePerson.getPerson("joanie", 14);public static void main(String[] args) {Thread t1 = new TestThread(1);t1.start();Thread t2 = new TestThread(2);t2.start();}}//a sample immutable class//Rule 4: define class as final onefinal class ImmutablePerson {private final int age; //Rule 1: all fields are private and finalprivate final String name;private ImmutablePerson(String name, int age) { //rule 2: a factory method pattern is adopted to create the objectthis.age = age;this.name = name;System.out.println(this);}public String getName() {return name;}public int getAge() {return age;}public String toString() {return name +": "+age +"year(s) old";}//Rule 3: no setters for value update. Create a new class insteadpublic static ImmutablePerson getPerson(String name, int age) {return new ImmutablePerson(name, age);} }class TestThread extends Thread {final int MAX=10; final int idx;public TestThread(int idx) {this.idx = idx;}public void run() {for (int i = 0; i < MAX; i++) {ImmutableDemo.test = ImmutablePerson.getPerson("joanie"+idx, idx*MAX+i);try {Thread.sleep(20+i*2);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}}运行结果:
joanie1: 10year(s) old
joanie2: 20year(s) old
joanie1: 11year(s) old
joanie2: 21year(s) old
joanie1: 12year(s) old
joanie2: 22year(s) old
joanie1: 13year(s) old
joanie2: 23year(s) old
joanie1: 14year(s) old
joanie2: 24year(s) old
joanie1: 15year(s) old
joanie2: 25year(s) old
joanie1: 16year(s) old
joanie2: 26year(s) old
joanie1: 17year(s) old
joanie2: 27year(s) old
joanie1: 18year(s) old
joanie2: 28year(s) old
joanie1: 19year(s) old
joanie2: 29year(s) old
使用Immutable不得不提到的一个问题是:由于其使用创建新对象来代替setter/update,势必会造成过多垃圾回收的对象。因此,为了性能的考虑,往往在为某个对象提供其Immutable实现的同时,还需提供一个它的Mutable伴侣,就像StringBuffer相对于String,使用这些值对象时就需要综合考虑了。
参考资料:
1. Effective Java 第二版
2. http://www.artima.com/designtechniques/threadsafety5.html
3. Java concurrency in Practice
- 使用Immutable对象解决线程安全
- 使用Monitor,Synchronization Domains, ReadWriteLock,和Immutable对象实现线程安全
- 从Immutable来谈谈对于线程安全的理解误区
- Immutable对象
- 对象使用中的线程安全和线程不安全问题
- FILE对象线程安全
- 使用正确的锁对象来保证线程安全
- Java并发编程规则:有状态的线程安全对象在线程池中使用不一定就是线程安全的
- SimpleDateFormat 解决非线程安全
- 同步函数解决线程安全
- 线程安全的对象操作
- 非线程安全对象池
- 线程安全的对象池
- 一些线程安全的对象
- 线程安全和对象共享
- 线程通信,线程安全及解决方式
- 利用Immutable解决React-Native那些因为对象被篡改导致的多次render问题
- 利用Immutable解决React-Native那些因为对象被篡改导致的多次render问题
- gui
- Spring 和struts 整合的三种方式
- 读书笔记 -- 《瓦尔登湖》
- 黑马程序员:java课程要点-面向对象
- JavaScript就这么回事
- 使用Immutable对象解决线程安全
- APEX文件上传下载
- 第9章 表和约束
- Spring加载Hibernate 映射的几种方式及区别
- ecshop与shopex功能对比
- Mac系统下编译Oracle的SQL Drivers插件qsqloci
- eclipse plugin develop
- 调用其他程序,一起运行
- linux 中的分区是什么样子