1.不可变对象模式(Immutable)
(1)描述
- 不可变对象一定是线程安全的,可变对象不一定是线程安全的
- servlet不是线程安全的,所有的请求对应的都是一个servlet实例,所以写servlet不能有一些全局的变量
- strusts 1.x Action不是线程安全的,
- strusts 2.x Action是线程安全的,
- JDK官方对定义一个不可变对象描述如下:
String就是典型的不可变对象
package Immutable;
public class StringTest {
public static void main(String[] args) {
String s1 = "hello";
String s2 = s1.replace('l','s');
System.out.println(s1.getClass()+" "+s1.hashCode());
System.out.println(s2.getClass()+" "+s2.hashCode());
}
}
虽然能够替换成功,但是通过源码可以发现,它其实是通过将原来的字符复制并替换,最后新建对象,所以最终是不同的对象
并且注意官方也说了,符合上述规则的不一定就是不可变对象,如下示例:
package Immutable;
import java.util.ArrayList;
import java.util.List;
public class ImmutableTest {
private final String name;
private final int age;
/**
* 这里看似定义一个常量集合,实际上定义的是一个指向集合的常量引用
*/
private final List<String> list;
public ImmutableTest(String name, int age) {
this.name = name;
this.age = age;
//当ImmutableTest类构造的时候,list就固定指向这里创建的ArrayList集合,
// 后续不能再次更改该引用的指向,但是并未规定集合中的内容的改变
list = new ArrayList<>();
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
/**
* 这里实际是返回的list是引用,当用户获取到该引用后,
* 修改该引用执行的集合也就是上述的ArrayList中的内容还是可以的,
* 所以此对象不是不可变对象
*/
public List<String> getList() {
return list;
}
}
可以对getList方法做如下修改,该类的对象就是不可变对象了:
public List<String> getList() {
return Collections.unmodifiableList(list);
}
(2)自定义不可变对象代码示例
package Immutable;
/**
* 不可变对象
*/
public final class Person {
private final String name;
private final String address;
public Person(String name, String address) {
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 Immutable;
public class UserPersonThread extends Thread{
Person person;
public UserPersonThread(Person person) {
this.person = person;
}
@Override
public void run() {
while(true){
System.out.println(Thread.currentThread().getName()+" print "+person.toString());
}
}
}
package Immutable;
import java.util.stream.IntStream;
public class ImmutableClient {
public static void main(String[] args) {
//共享数据
Person person = new Person("Alex","Gansu");
IntStream.range(0,5).forEach(i->
new UserPersonThread(person).start()
);
}
}
- 运行多少次都是线程安全的,因为我们不可以去更改不可变的共享资源对象,即对共享资源不会发生写操作,所以可以保证线程安全