一. 不可变对象
不可变对象是线程安全的,因为没有线程能够修改它。通过 String 类发现不可变类有以下几个特征:
- 私有变量为 final 的,保证只有在构造时才能给它赋值;
- 类也被 final 修饰,保证子类不会破坏其线程安全性;
- 保护性拷贝(复制一份新的副本避免共享)。
二. 享元模式
保护性拷贝每次都会创建新的对象,占用了大量内存。设计模式中的享元模式是通过重用取值相同的对象,来减少内存的使用。
1. 包装类
Byte、Short、Long 等类中的 valueOf() 方法中有一个 cache 数组存储了 -128-127 的数值,在这个范围内的数值都进行共享,只有超出了这个范围的数值才会创建新的。
Character 的范围是 0 -127
Integer 的默认范围是 -128 - 127
Boolen :true、false
String 串池
BigDecimal、BigInteger
2. 实例 - 自定义连接池
以下实现了一个简单的自定义连接池,池中的连接是先创建好的,线程需要时从池中获取,使用完后归还。避免频繁创建和关闭连接带来的资源损耗,也是享元模式的典型应用。
class Pool {
private static final Logger logger = LoggerFactory.getLogger(Pool.class);
//连接池大小
private final int poolSize;
//连接池数组
private Connection[] connectionArray;
//连接池数组状态,需要保证线程安全, 0-空闲,1-繁忙
private AtomicIntegerArray stateArray;
public Pool(int poolSize) {
this.poolSize = poolSize;
this.connectionArray = new Connection[poolSize];
this.stateArray = new AtomicIntegerArray(new int[poolSize]);
for (int i = 0; i < poolSize; i++) {
connectionArray[i] = new MockConnection("连接" + (i+1));
}
}
//连接借出
public Connection borrow() {
while(true) {
for (int i = 0; i < poolSize; i++ ) {
if (stateArray.compareAndSet(i, 0, 1)) {
logger.info("获取到" + connectionArray[i].toString());
return connectionArray[i];
}
}
//避免一直空转
synchronized (this) {
try {
logger.info("进入等待……");
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//连接归还
public void free(Connection connection) {
for (int i = 0; i < poolSize; i++) {
if (connection == connectionArray[i]) {
logger.info("归还" + connectionArray[i].toString());
stateArray.set(i, 0);
synchronized (this) {
this.notifyAll();
}
break;
}
}
}
}
//自定义连接类
class MockConnection implements Connection {
private String name;
public MockConnection(String name) {
this.name = name;
}
@Override
public String toString() {
return "MockConnection{" +
"name='" + name + '\'' +
'}';
}
…………………………
}
测试:
public static void main(String[] args) {
Pool pool = new Pool(2);
for (int i = 0 ; i < 5; i++) {
new Thread(() -> {
Connection conn = pool.borrow();
try {
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
pool.free(conn);
}).start();
}
}