JAVA - 并发之不可变与享元与连接池

不可变
日期类的转换问题


加锁解决

日期转换的线程安全类

不可变类举例 - String类





享元模式



BigDecimal也是使用了保护性拷贝

不可变类的线程安全问题分析
写个简单的连接池,体会享元模式
public class Test3 {
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(3000));
} catch (InterruptedException e) {
throw new RuntimeException(e);
}finally {
pool.free(conn);
}
},"t"+i).start();
}
}
}
@Slf4j(topic = "c.test")
class Pool{
//连接池大小
private final int poolSize;
//连接对象数组
private Connection [] connections;
//每个连接对象的状态,0-空闲,1-繁忙,此处需要考虑并发,比如从繁忙到空闲,从空闲到繁忙
private AtomicIntegerArray states;
public Pool(int poolSize){
this.poolSize=poolSize;
this.connections=new Connection[poolSize];
this.states = new AtomicIntegerArray(poolSize);
for (int i=0;i<poolSize;i++){
connections[i]=new MockConnection("连接-"+i);
}
}
public Connection borrow(){
//要一直循环,如果不是循环,那么会有一种情况,没有获取到连接对象,遍历完后或者唤醒后,方法就结束了,这样调用方又要再调一次。一直循环,就是直到获取到为止。
while (true){
//
for(int i=0;i<poolSize;i++){
if (states.get(i)==0){
//此处就体现了为什么状态要使用原子类,比如线程1和线程2同时获取到某个连接的状态0,相当于两个都获取到连接,因此,此时需要cas操作,有一个成功,有一个失败。
if (states.compareAndSet(i,0,1)){
log.debug("borrow {}",connections[i]);
return connections[i];
}
}
}
//获取不到,进入等待。没有下面这段代码也是可行的,但是,可能会有这种情况,获取连接对象是为了连接数据库做增删改查,时间会比较久,不等待的话,就会一直for循环获取连接,就会使CPU空转
synchronized (this){
try {
log.debug("wait....");
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//释放连接对象,此处没有并发操作,因为是由持有该连接对象的线程来释放,没有其他线程来干扰
public void free(Connection conn){
for (int i =0;i<poolSize;i++){
if (connections[i]==conn){
states.set(i,0);
synchronized (this){
log.debug("free:{}",conn);
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 + '\'' +
'}';
}
//省略了要实现的方法
//...
}
final原理

如果没有final,即int a= 20,会有线程安全的问题,因为int a = 20 ,有两步操作,第一步a默认为0,第二步设a为20(此处不懂可以去看jvm的笔记),如果没有final,多线程下可能会读取到0

final修饰,执行速度快

没有final要准备10,然后getstatic,速度慢

浙公网安备 33010602011771号