基本概念

有状态bean(Stateful Bean):有数据存储功能,每个用户有自己特有的一个实例,在用户的生存期内,bean 保持了用户的信息,即“有状态”;一旦调用结束或实例结束,bean 的生命期也将结束,是非线程安全的。

无状态bean(Stateless Bean):bean一旦实例化就被加进会话池中,各个用户都可以共用;即使用户已经消亡,bean 的生命期也不一定结束,它可能依然存在于会话池中,供其他用户调用;是线程安全的。

代码示例

有状态Bean代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class B {
private String name;
public B(String arg) {
this.name = arg;
}
public String sayHello() {
return "Hello" + this.name;
}
}

public class Client {
public Client() {
B a = new B("MinHow");
System.out.println(a.sayHello());
B b = new B("Marry");
System.out.println(b.sayHello());
}
}

Client中生成了两个B的实例,两个实例输出的结果是不一样的,它是有存储能力的,也就是说至少有一个属性来标识它目前的状态。
无状态Bean代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class A {
public A() {}
public String sayHello() {
return "Hello MinHow";
}
}
public class Client {
public Client() {
A a = new A();
System.out.println(a.sayHello());
A b = new A();
System.out.println(b.sayHello());
}
}

Client中生成了两个A的实例,不管是对象a还是b,它们是没有状态的。对于Client来说a和b是没有差别的。所以同一个无状态会话Bean的实例都是相同的,可以被不同的客户端重复使用。

Spring 中的有状态(Stateful)和无状态(Stateless)

  1. 通过上面的分析,相信大家已经对有状态和无状态有了一定的理解。无状态的Bean适合用不变模式,技术就是单例模式,这样可以共享实例,提高性能。有状态的Bean,多线程环境下不安全,那么适合用Prototype原型模式。Prototype: 每次对bean的请求都会创建一个新的bean实例。
  2. 默认情况下,从Spring bean工厂所取得的实例为singleton(scope属性为singleton),容器只存在一个共享的bean实例。
  3. 理解了两者的关系,那么scope选择的原则就很容易了:有状态的bean都使用prototype作用域,而对无状态的bean则应该使用singleton作用域。
  4. 如Service层、Dao层用默认singleton就行,虽然Service类也有dao这样的属性,但dao这些类都是没有状态信息的,也就是相当于不变(immutable)类,所以不影响。Struts2中的Action因为会有User、BizEntity这样的实例对象,是有状态信息的,
    在多线程环境下是不安全的,所以Struts2默认的实现是Prototype模式。在Spring中,Struts2的Action中,scope要配成prototype作用域。

Spring 使用 ThreadLocal 解决线程安全问题

我们知道在一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域。就是因为Spring对一些Bean(如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)中非线程安全状态采用ThreadLocal进行处理,让它们也成为线程安全的状态,因为有状态的Bean就可以在多线程中共享了。
一般的Web应用划分为展现层、服务层和持久层三个层次,在不同的层中编写对应的逻辑,下层通过接口向上层开放功能调用。在一般情况下,从接收请求到返回响应所经过的所有程序调用都同属于一个线程 ThreadLocal 是解决线程安全问题一个很好的思路,它通过为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题。
在很多情况下,ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性。

线程安全问题都是由全局变量及静态变量引起的

若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则就可能影响线程安全。
1)常量始终是线程安全的,因为只存在读操作。
2)每次调用方法前都新建一个实例是线程安全的,因为不会访问共享的资源。
3)局部变量是线程安全的。因为每执行一个方法,都会在独立的空间创建局部变量,它不是共享的资源。局部变量包括方法的参数变量和方法内变量。

最后更新: 2019年08月04日 18:09

原始链接: http://blog.minhow.com/articles/java/stateful-stateless-bean/

× 请我吃糖~
打赏二维码