Immutable
Immutable(不可变的),Immutable角色是一个类,在这个角色中,字段的值不可修改,也不存在修改字段内容的方法。Immutable角色的实例被创建后,状态将不再发生变化。无需将Immutable角色的方法声明为synchronized
Immutable模式的类图:
何时使用Immutable:
1. 实例创建后,状态不再发生变化时
字段声明为final,不存在setter方法
2. 实例是共享的,且被频繁访问时
不需要使用synchronized,这样就能提高性能。
java标准库类中有成对的mutable和immutable类
java.lang.StringBuffer (mutable类)和java.lang.String(immutable类)
所以如果需要频繁修改字符串内容,则使用StringBuffer(改写时需要妥善使用synchronized);
如果不需要修改字符串内容,只是引用其内容。
标准类库中用到的Immutable模式:
表示字符串的java.lang.String类
表示大数字的java.math.BigInteger类,java.math.BigInteger
表示正则表达式的java.util.regex.Pattern类等
非线程安全的java.util.ArrayList的类
一下是实例代码
public class WriterThread extends Thread{
private final List<Integer> list;
public WriterThread(List<Integer> list) {
super();
this.list = list;
}
public void run(){
for (int i = 0;true; i++) {
list.add(i);
list.remove(0);
}
}
}
public class ReaderThread extends Thread{
private final List<Integer> list;
public ReaderThread(List<Integer> list) {
super();
this.list = list;
}
public void run(){
while (true) {
for (int n:list) {
System.out.println(n);
}
}
}
}
public class Main {
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>();
new WriterThread(list).start();
new ReaderThread(list).start();
}
}
当ArrayList在被多个线程同时进行读写操作时而失去安全性时,便会抛出
Exception in thread "Thread-1" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
at java.util.ArrayList$Itr.next(Unknown Source)
at day2.ReaderThread.run(ReaderThread.java:15)
利用Collections.synchronizedList方法进行的同步
java.util.ArrayList是非线程安全的类,但如果使用Collections.sysnchronizedList方法进行同步,就能得到线程安全的实例。
public class ReaderThread extends Thread{
private final List<Integer> list;
public ReaderThread(List<Integer> list) {
super();
this.list = list;
}
public void run(){
while(true){
synchronized (list) {
for(int n:list){
System.out.println(n);
}
}
}
}
}
public class Main {
public static void main(String[] args) {
//final只是说你的当前这个list不能指向内存其他地方了
final List<Integer> list= Collections.synchronizedList(new ArrayList<Integer>());
new WriterThread(list).start();
new ReaderThread(list).start();
}
}
WriterThread与前面例子代码用例一致,因为写线程不断改写值,所以读取的值是跳跃的
CopyOnWriteArrayList类
采用copy-on-write技术避免读写冲突,所谓copy-on-write,就是写时复制的意思,当集合执行写操作的时候,内部已确保安全的数组就会被整体复制。
实例代码:读写线程类与ArrayList的实例相同
public class Main {
public static void main(String[] args) {
final List<Integer> list = new CopyOnWriteArrayList<Integer>();
new ReaderThread(list).start();
new WriterThread(list).start();
}
}
Immutable模式:
当一个类的实例创建完成后,其状态就完全不会发生变化。这时,该类的方法就算被多个线程同时执行也没关系,所以这些方法就无需声明为synchronized,这样就可以在生存型和安全性不缺失的同时提高性能。
下面的s被replace替换成了CAT,有人觉得这样违背了immutable的模式,实际上着个字符串s没有变,只是replace这个方法新建了一个实例而已。
public static void main(String[] args) {
String s = “BAT”;
System.out.print(s.replace(‘B’,’C’));
}
如下代码,getInfo方法获取的info字段中保存的实例并不是String实例,而是StringBuffer实例,StringBuffer类与String类不同,包含修改内部状态的方法,所以info子弹的内容也可以被外部修改,String类的replace方法并不会修改实例本身,但StringBuffer的replace会修改实例本身,由于info字段声明了final,所以info字段的值本身并不会改变,但是info字段所指向的实例的状态却可能发生改变。
public final class UserInfo {
private final StringBuffer info;
public UserInfo(String name,String address){
this.info = new StringBuffer("<info name=\""+name+"\"address=\""+address+"\"+/>");
}
public StringBuffer getInfo(){
return info;
}
public String toString(){
return "[userInfo:"+info+"]";
}
}
public static void main(String[] args) {
UserInfo userinfo = new UserInfo("Alice", "America");
System.out.println("userinfo="+userinfo);
//修改状态
StringBuffer info = userinfo.getInfo();
System.out.println(info);
info.replace(12, 17, "Boddy");
System.out.println("userinfo="+userinfo);
}