第二章 Immutable模式 想破坏也破坏不了

【2.1 Immutable模式】

Java.lang.String类用于表示字符串。String类中并没有修改字符串内容的方法也就是说,String的实例所表示的字符串的内容绝对不会发生变化。

因此String类中的方法无需声明为synchronized。因为实例内部状态不会发生改变,所以无论String实例被多个线程访问,也无需执行线程的互斥处理。

Immutable就是不变的、不发生改变的意思。Immutable模式中存在着确保实例状态不发生改变的类。

【2.2示例程序】

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 address() {

          return address;

     }

     public String toString() {

          return "[ Person:name = "+name+", address = "+address+" ]";

     }

}

Person类声明为了final类型,这就表示我们无法创建Person类的子类。这并不是Immutable模式的必要条件,但却是防止子类修改其字段的一种措施。

Person类的字段name和address的可见性都为private。也就是说,这两个字段都只有该类的内部才可以访问。这也不是Immutable模式的必要条件,而是防止子类修改其字段的一种措施。

另外,Person类的字段name和address都声明为了final,意思是一旦字段被赋值一次,就不会再被赋值。这也不是Immutable模式的必要条件。

public class PrintPersonThread extends Thread {

     private Person person;

     public PrintPersonThread(Person person){

          this.person=person;

     }

     public void run() {

          while(true) {

               System.out.println(Thread.currentThread().getName()+" prints "+person);

          }

     }

}

Thread.currentThread().getName()用于获取自身线程的名称

字符串和实例表达式通过“+”运算符连接时,程序会自动调用实例表达式的toString方法。

【2.3 Immutable模式中登场的角色】

Immutable角色是一个类,在这个角色中,字段的值不可以修改,也不存在修改字段内容的方法。Immutable角色的实例被创建后,状态将不再发生变化。这时,无需对Immutable角色应用Single Threaded Execution模式,也就是说,无需将Immutable角色的方法声明为synchronized.

 

【2.4 标准类库中用到的Immutable模式】

String,BigInteger,Pattern,Integer

 

【2.5 相关的设计模式】

Single Threaded Execution模式

在Immutable模式中,实例的状态不发生变化,所以无需进行保护。

而在Single Threaded Execution模式中,当一个线程正在修改实例状态时,不允许其他的线程来访问该实例。此时会出现如下两种情况之一:

    ·写入与写入的冲突(write-write conflict)

    当一个线程正在修改实例状态(write),而其他线程也在试图修改其状态(write)时发生的冲突。

    ·读取与写入的冲突(read-write conflict)

    当一个线程正在读取实例状态(read),而其他线程也在试图修改其状态(write)时发生的冲突。

Read-Write Lock模式

    在Immutable模式中,多个线程之间只会发生read-read的情况。因此,多个线程可以自由地访问实例。

    Read-Write Lock模式也利用了read-read不会引起conflict的特点。在Read-Write Lock模式中,执行read的线程和执行write的线程是分开考虑的。发生write-wrte conflict或read-write colflict时,需要执行线程的互斥处理,而发生read-read时就不需要执行线程的互斥处理,这会提高程序性能。

public class Main1 {

     public static void main(String[] args) {

          List<Integer> list = new ArrayList<Integer>();

          new WriterThread(list).start();

          new ReaderThread(list).start();

     }

}

public class WriterThread extends Thread {

     private final List<Integer> list;

     public WriterThread(List<Integer> list) {

          super("WriterThread");

          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("ReaderThread");

          this.list=list;

     }

     public void run() {

          while(true) {

              for(int n:list) {

                   System.out.println(n);

              }

          }

     }

}

ArrayList类在被多个线程同时读取而失去安全性时,便会抛出ConcurrentModificationException异常

java.util.ArrayList是非线程安全的类,但如果使用Collections.synchronizedList方法进行同步,就能得到线程安全的实例。

public class Main1 {

     public static void main(String[] args) {

          final List<Integer> list = Collections.synchronizedList(new ArrayList<Integer>());

          new WriterThread(list).start();

          new ReaderThread(list).start();

     }

}

public class ReaderThread extends Thread {

     private final List<Integer> list;

     public ReaderThread(List<Integer> list) {

          super("ReaderThread");

          this.list=list;

     }

     public void run() {

          while(true) {

              synchronized (list){

                   for(int n:list) {

                        System.out.println(n);

                   }

              }

          }

     }

}

写线程是显式调用add方法和remove方法,无需修改;读线程是隐式调用迭代器,需要修改其代码。

 

【练习题1 基础知识测试】

阅读下面的内容,叙述正确请打√,错误请打×。

1.java.lang.String类是immutable类。

2.java.lang.StringBuffer类是immutable类。

×。StringBuffer类是表示字符串的mutable类。StringBuffer表示的字符串能够随便改写,为了确保安全,改写时需要妥善使用sunchronized.

3.声明为final的字段不可以被赋值两次。

4.声明为private的字段可由所在类及子类直接访问。

×。仅可由声明该字段的类本身直接访问。

5.将方法声明为synchronized也不会有什么问题,所以应该尽可能地加上synchronized。

×。有可能会降低类的生存性或性能。

 

【练习题2 String类真的是immutable类吗】

答:s中保存的实例内容并没有被改写。replace方法会创建一个实例,用来保存替换后的字符串中的字符,并将该实例作为返回值。

public static void main(String[] args) {

      String s="BAT";

      System.out.println(s.replace('B', 'C'));

}

【练习题3 明明没有settter方法,却不是immutable类】

下面的UserInfo类用于表示用户的信息。在该类中,info字段为private final类型,也没用setter方法,但UserInfo类并不是immutable类,为什么?

public 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 +" ]";

     }

}

答:因为如果修改了getInfo方法的返回值,info字段所引用的实例内容也会发生变化。

public static void main(String[] args) {

    //创建实例

    UserInfo userinfo = new UserInfo("Alice","Alaska");

    //显示

    System.out.println("userinfo = "+userinfo);

    //修改状态

    StringBuffer info  = userinfo.getInfo();

    info.replace(12, 17, "Bobby");

    //再次显示

    System.out.println("userinfo = "+userinfo);

}

String类的replace方法并不会修改实例本身,但StringBuffer类的replace方法却会修改实例本身。由于info字段声明为了final,所以info字段的值本身并不会改变。但info字段所指向的实例的状态却有可能改变。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值