多线程不可变对象设计模式immutable

Posted zheaven

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程不可变对象设计模式immutable相关的知识,希望对你有一定的参考价值。

immutable特点:

1.不可变对象一定是线程安全的(里面的任何属性或者引用类型的属性一旦被初始化,都不能被修改)
2.可变对象不一定是不安全的 StringBuffer

题外话:

J2EE中
servlet 全局只创建一个对象,不是线程安全的
struts 1.x Action也不是线程安全的,只创建一个Action实例
struts 2.x Action是线程安全的,会为每一个请求创建一个Action实例

一个不可变对象类

package com.dwz.concurrency2.chapter8;

public final class Person {
    private final String name;
    private final String address;
    
    public Person(String name, String address) {
        super();
        this.name = name;
        this.address = address;
    }

    public String getName() {
        return name;
    }

    public String getAddress() {
        return address;
    }

    @Override
    public String toString() {
        return "Person [name=" + name + ", address=" + address + "]";
    }
}

使用不可变对象的线程类

package com.dwz.concurrency2.chapter8;

public class UsePersonThread extends Thread {
    private Person person;

    public UsePersonThread(Person person) {
        this.person = person;
    }
    
    @Override
    public void run() {
        while (true) {
            System.out.println(Thread.currentThread().getName() + " print " + person.toString());
        }
    }
}

测试

package com.dwz.concurrency2.chapter8;

import java.util.stream.IntStream;

public class ImmutableClient {
    public static void main(String[] args) {
        //share data
        Person person = new Person("Alex", "shanxi");
        IntStream.range(0, 5).forEach(i -> 
            new UsePersonThread(person).start()
        );
    }
}

不可变对象中对list引用的处理

package com.dwz.concurrency2.chapter8;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public final class ImmutableTest {
    private final int age;
    private final String name;
    private final List<String> list;
    public ImmutableTest(int age, String name) {
        this.age = age;
        this.name = name;
        this.list = new ArrayList<>();
    }
    public int getAge() {
        return age;
    }
    public String getName() {
        return name;
    }
    public List<String> getList() {
        return Collections.unmodifiableList(list);
    }
}

举例说明在实现线程安全方面,不可变对象和synchronized的性能差异

package com.dwz.concurrency2.chapter8;

public class ImmutablePerformance {
    public static void main(String[] args) throws InterruptedException {
        long startTimeStamp = System.currentTimeMillis();
        //SyncObj 3742     单线程
        SyncObj syncobj = new SyncObj();
        syncobj.setName("Alex");
        for(long i = 0L; i < 1000000; i++) {
            System.out.println(syncobj.toString());
        }
        
        //ImmutableObj 3365 单线程
        ImmutableObj ImmutableObj = new ImmutableObj("dandan");
        for(long i = 0L; i < 1000000; i++) {
            System.out.println(ImmutableObj.toString());
        }
        
        //SyncObj 10009    多线程 16963
        //ImmutableObj 9452 多线程 16750
        Thread t1 = new Thread() {
            @Override
            public void run() {
                for(long i = 0L; i < 3000000; i++) {
                    System.out.println(Thread.currentThread().getName() + "=" + ImmutableObj.toString());
                }
            }
        };
        
        Thread t2 = new Thread() {
            @Override
            public void run() {
                for(long i = 0L; i < 1000000; i++) {
                    System.out.println(Thread.currentThread().getName() + "=" + ImmutableObj.toString());
                }
            }
        };
        
        t1.start();
        t2.start();
        
        t1.join();
        t2.join();
        long endTimeStamp = System.currentTimeMillis();
        System.out.println("Elapsed time " + (endTimeStamp - startTimeStamp));
    }
}

final class ImmutableObj {
    private final String name;
    
    public ImmutableObj(String name) {
        this.name = name;
    }
    
    @Override
    public String toString() {
        return "[" + name + "]";
    }
}

class SyncObj {
    private String name;
    
    public synchronized void setName(String name) {
        this.name = name;
    }
    
    @Override
    public synchronized String toString() {
        return "[" + name + "]";
    }
}

结果表明:不可变对象确实比synchronized性能有所提升

那么在实现自定义的Immutable的Class的时候,应该注意哪些要点呢?
a)Class 应该定义成final,避免被继承。
b)所有的成员变量应该被定义成final。
c)不要提供可以改变类状态(成员变量)的方法。【get 方法不要把类里的成员变量让外部客服端引用,当需要访问成员变量时,返回成员变量的copy】
d)构造函数不要引用外部可变对象。如果需要引用外部可以变量,应该在构造函数里进行defensive copy。

以上是关于多线程不可变对象设计模式immutable的主要内容,如果未能解决你的问题,请参考以下文章

多线程----Immutable VS Mutable (可变与不可变)

JAVA多线程模式-Immutable

线程安全

Java多线程编程模式实战指南:Immutable Object模式

Java的线程安全

Guava集合--Immutable(不可变)集合